import { useState, useEffect, useRef } from 'react'; interface UseAnimatedCounterOptions { /** Final value to count to */ end: number; /** Duration of the animation in ms */ duration?: number; /** Whether the animation should start (default: true) */ enabled?: boolean; /** Number of decimal places */ decimals?: number; } export function useAnimatedCounter({ end, duration = 1000, enabled = true, decimals = 0, }: UseAnimatedCounterOptions): number { const [count, setCount] = useState(0); const frameRef = useRef(); const startTimeRef = useRef(); useEffect(() => { if (!enabled || end === 0) { setCount(end); return; } const animate = (timestamp: number) => { if (!startTimeRef.current) startTimeRef.current = timestamp; const progress = Math.min((timestamp - startTimeRef.current) / duration, 1); // Ease-out cubic const eased = 1 - Math.pow(1 - progress, 3); const current = eased * end; setCount(Number(current.toFixed(decimals))); if (progress < 1) { frameRef.current = requestAnimationFrame(animate); } }; frameRef.current = requestAnimationFrame(animate); return () => { if (frameRef.current) cancelAnimationFrame(frameRef.current); }; }, [end, duration, enabled, decimals]); return count; }