import { ChangeDetectionStrategy, Component, Input, OnChanges, SimpleChanges, ElementRef, ViewChild, AfterViewInit, } from '@angular/core'; import { CommonModule, CurrencyPipe } from '@angular/common'; import { CountUp } from 'countup.js'; @Component({ selector: 'app-animated-number', standalone: true, imports: [CommonModule, CurrencyPipe], template: ` {{ formattedValue }} `, changeDetection: ChangeDetectionStrategy.OnPush, }) export class AnimatedNumberComponent implements OnChanges, AfterViewInit { @Input() value = 0; @Input() duration = 1; @Input() ease = 'power1.out'; @ViewChild('numberElement') numberElement!: ElementRef; private countUp: CountUp | null = null; private previousValue = 0; formattedValue = '0,00 €'; ngAfterViewInit(): void { this.initializeCountUp(); if (this.countUp && this.value !== 0) { this.countUp.start(() => { this.previousValue = this.value; }); } } ngOnChanges(changes: SimpleChanges): void { if (changes['value']) { if (this.countUp) { const endVal = this.value; this.countUp.update(endVal); this.previousValue = endVal; } else { this.formattedValue = new Intl.NumberFormat('de-DE', { style: 'currency', currency: 'EUR', minimumFractionDigits: 2, maximumFractionDigits: 2, }).format(this.value); } } } private initializeCountUp(): void { if (this.numberElement) { this.countUp = new CountUp(this.numberElement.nativeElement, this.value, { startVal: this.previousValue, duration: this.duration, easingFn: (t, b, c, d) => { if (this.ease === 'power1.out') { return c * (1 - Math.pow(1 - t / d, 1)) + b; } return c * (t / d) + b; }, formattingFn: (value) => { const formatted = new Intl.NumberFormat('de-DE', { style: 'currency', currency: 'EUR', minimumFractionDigits: 2, maximumFractionDigits: 2, }).format(value); this.formattedValue = formatted; return formatted; }, }); } } }