"use client"; import { useState, useEffect, useRef } from "react"; export interface AnimatedCounterProps { value: number; duration?: number; prefix?: string; suffix?: string; formatter?: (value: number) => string; } export function AnimatedCounter({ value, duration = 2000, prefix = "", suffix = "", formatter }: AnimatedCounterProps) { const [displayValue, setDisplayValue] = useState(0); const startTimeRef = useRef(null); const frameRef = useRef(null); // Easing function for smoother animation const easeOutQuad = (t: number): number => t * (2 - t); useEffect(() => { // Reset start time on new value startTimeRef.current = null; // Cancel any ongoing animation if (frameRef.current) { cancelAnimationFrame(frameRef.current); } // Animation function const animate = (timestamp: number) => { if (!startTimeRef.current) { startTimeRef.current = timestamp; } const elapsed = timestamp - startTimeRef.current; const progress = Math.min(elapsed / duration, 1); const easedProgress = easeOutQuad(progress); const nextValue = Math.floor(easedProgress * value); setDisplayValue(nextValue); if (progress < 1) { frameRef.current = requestAnimationFrame(animate); } else { setDisplayValue(value); } }; // Start the animation frameRef.current = requestAnimationFrame(animate); return () => { if (frameRef.current) { cancelAnimationFrame(frameRef.current); } }; }, [value, duration]); const formattedValue = formatter ? formatter(displayValue) : `${prefix}${displayValue.toLocaleString()}${suffix}`; return {formattedValue}; }