import { useEffect, useRef } from 'react'; import { useInView, useMotionValue, useSpring } from 'framer-motion'; export function AnimatedCounter({ value, format = 'number', className = '' }: { value: number | string; format?: 'number' | 'currency' | 'percent'; className?: string; }) { const ref = useRef(null); const motionValue = useMotionValue(0); const springValue = useSpring(motionValue, { stiffness: 50, damping: 20, mass: 1, }); const isInView = useInView(ref, { once: true, margin: '-50px' }); useEffect(() => { if (isInView) { const numericValue = typeof value === 'string' ? parseFloat(value.replace(/[^0-9.-]+/g, '')) : value; if (!isNaN(numericValue)) { motionValue.set(numericValue); } } }, [value, isInView, motionValue]); useEffect(() => { const unsubscribe = springValue.on('change', (latest) => { if (ref.current) { if (format === 'currency') { ref.current.textContent = `¥${latest.toFixed(2)}`; } else if (format === 'percent') { ref.current.textContent = `${latest.toFixed(1)}%`; } else { ref.current.textContent = Math.round(latest).toLocaleString(); } } }); return () => unsubscribe(); }, [springValue, format]); return ( {typeof value === 'string' && isNaN(parseFloat(value.replace(/[^0-9.-]+/g, ''))) ? value : (format === 'currency' ? '¥0.00' : '0')} ); }