@omnedia/ngx-number-ticker
Version:
A simple number ticker effect to animate counting.
163 lines (158 loc) • 7.68 kB
JavaScript
import * as i1 from '@angular/common';
import { CommonModule } from '@angular/common';
import * as i0 from '@angular/core';
import { signal, Input, ChangeDetectionStrategy, Component } from '@angular/core';
class NgxNumberTickerComponent {
styleClass;
set countTo(number) {
this.targetNumber = number;
this.countToNumber(number);
}
set countDuration(countDuration) {
this.countDurationValue = countDuration;
if (this.frameId) {
cancelAnimationFrame(this.frameId);
this.frameId = undefined;
this.countToNumber(this.targetNumber);
}
}
countDurationValue = 2000;
transformFunction;
targetNumber = 0;
currentNumber = 0;
displayNumber = signal("0");
frameId;
countToNumber(number) {
if (number > this.currentNumber) {
this.countUp(number);
return;
}
this.countDown(number);
}
countUp(target) {
const direction = "up";
this.executeCount(target, direction);
}
countDown(target) {
const direction = "down";
this.executeCount(target, direction);
}
setCountValue() {
if (!this.transformFunction) {
this.displayNumber.set(`${this.currentNumber}`);
}
else {
this.displayNumber.set(this.transformFunction(this.currentNumber));
}
}
executeCount(target, direction) {
const startNumber = this.currentNumber;
const difference = Math.abs(target - startNumber);
// Check if we have fewer than 10 digits to count
const isSmallDifference = difference <= 10;
const duration = this.countDurationValue;
// If the difference is small, handle all of them in the easing phase
if (isSmallDifference) {
const slowCountingDuration = duration;
const startTime = performance.now();
const updateSmallDifference = (currentTime) => {
const elapsed = currentTime - startTime;
const percentComplete = Math.min(elapsed / slowCountingDuration, 1);
const easedPercent = this.easeOutQuad(percentComplete);
// Calculate the current number based on the eased percent
this.currentNumber = this.calculateCurrentNumber(startNumber, target, easedPercent, direction);
if (elapsed < slowCountingDuration) {
this.setCountValue();
this.frameId = requestAnimationFrame(updateSmallDifference);
}
else {
this.currentNumber = target; // Ensure final number is set
this.setCountValue();
}
};
this.frameId = requestAnimationFrame(updateSmallDifference);
}
else {
// Normal case: Handle large differences with two phases
const last10DigitsDuration = 1000; // 1 second for the last 10 digits
const mainCountingDuration = duration - last10DigitsDuration;
const last10Start = direction === "up" ? target - 10 : target + 10;
const startTime = performance.now();
const update = (currentTime) => {
const elapsed = currentTime - startTime;
if ((direction === "up" && this.currentNumber < last10Start) ||
(direction === "down" && this.currentNumber > last10Start)) {
// Phase 1: Main counting phase
const percentComplete = Math.min(elapsed / mainCountingDuration, 1);
// Calculate current number based on the percentage of completion
this.currentNumber = this.calculateCurrentNumber(startNumber, last10Start, percentComplete, direction);
if (elapsed < mainCountingDuration) {
this.setCountValue();
this.frameId = requestAnimationFrame(update);
}
else {
this.currentNumber = last10Start;
this.setCountValue();
this.frameId = requestAnimationFrame(update);
}
}
else {
// Phase 2: Slow down for the last 10 digits
const elapsedForLast10 = elapsed - mainCountingDuration;
const percentComplete = Math.min(elapsedForLast10 / last10DigitsDuration, 1);
const easedPercent = this.easeOutQuad(percentComplete);
// Calculate the number for the last 10 digits
this.currentNumber = this.calculateCurrentNumber(last10Start, target, easedPercent, direction);
if (elapsedForLast10 < last10DigitsDuration) {
this.setCountValue();
this.frameId = requestAnimationFrame(update);
}
else {
this.currentNumber = target; // Ensure final number is set
this.setCountValue();
}
}
};
this.frameId = requestAnimationFrame(update);
}
}
// Helper function to calculate current number based on direction
calculateCurrentNumber(start, end, percent, direction) {
if (direction === "up") {
return Math.floor(start + (end - start) * percent);
}
else {
return Math.floor(start - (start - end) * percent);
}
}
// Easing function to slow down progressively
easeOutQuad(t) {
return t * (2 - t);
}
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.0.2", ngImport: i0, type: NgxNumberTickerComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "20.0.2", type: NgxNumberTickerComponent, isStandalone: true, selector: "om-number-ticker", inputs: { styleClass: "styleClass", countTo: "countTo", countDuration: "countDuration", transformFunction: "transformFunction" }, ngImport: i0, template: "<p class=\"om-number-ticker\" [ngClass]=\"styleClass\">\r\n {{ displayNumber() }}\r\n</p>\r\n", styles: [".om-number-ticker{width:fit-content}\n"], dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "directive", type: i1.NgClass, selector: "[ngClass]", inputs: ["class", "ngClass"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush });
}
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.0.2", ngImport: i0, type: NgxNumberTickerComponent, decorators: [{
type: Component,
args: [{ selector: "om-number-ticker", standalone: true, imports: [CommonModule], changeDetection: ChangeDetectionStrategy.OnPush, template: "<p class=\"om-number-ticker\" [ngClass]=\"styleClass\">\r\n {{ displayNumber() }}\r\n</p>\r\n", styles: [".om-number-ticker{width:fit-content}\n"] }]
}], propDecorators: { styleClass: [{
type: Input,
args: ["styleClass"]
}], countTo: [{
type: Input,
args: ["countTo"]
}], countDuration: [{
type: Input,
args: ["countDuration"]
}], transformFunction: [{
type: Input,
args: ["transformFunction"]
}] } });
/*
* Public API Surface of ngx-number-ticker
*/
/**
* Generated bundle index. Do not edit.
*/
export { NgxNumberTickerComponent };
//# sourceMappingURL=omnedia-ngx-number-ticker.mjs.map