UNPKG

@omnedia/ngx-number-ticker

Version:

A simple number ticker effect to animate counting.

163 lines (158 loc) 7.68 kB
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