From 76111ecd2d56001ded3b353e17749a7bb667f9bc Mon Sep 17 00:00:00 2001 From: count0 Date: Wed, 15 Oct 2025 18:23:56 +0200 Subject: [PATCH] Add juice to the counter animations --- src/components/AnimatedCounter.jsx | 22 +++++++++++++++++++++- 1 file changed, 21 insertions(+), 1 deletion(-) diff --git a/src/components/AnimatedCounter.jsx b/src/components/AnimatedCounter.jsx index d5961eb..c601840 100644 --- a/src/components/AnimatedCounter.jsx +++ b/src/components/AnimatedCounter.jsx @@ -7,14 +7,19 @@ import React, { useEffect, useRef, useState } from 'react'; */ function AnimatedCounter({ value, duration = 1000, start = 0, format = v => v }) { const [displayValue, setDisplayValue] = useState(start); + const [animating, setAnimating] = useState(false); + const [direction, setDirection] = useState('up'); const rafRef = useRef(); const startRef = useRef(start); const valueRef = useRef(value); + const prevValueRef = useRef(start); useEffect(() => { startRef.current = displayValue; valueRef.current = value; let startTime; + setAnimating(true); + setDirection(value > prevValueRef.current ? 'up' : value < prevValueRef.current ? 'down' : direction); function animate(ts) { if (!startTime) startTime = ts; const progress = Math.min((ts - startTime) / duration, 1); @@ -22,13 +27,28 @@ function AnimatedCounter({ value, duration = 1000, start = 0, format = v => v }) setDisplayValue(current); if (progress < 1) { rafRef.current = requestAnimationFrame(animate); + } else { + setAnimating(false); + prevValueRef.current = current; } } rafRef.current = requestAnimationFrame(animate); return () => cancelAnimationFrame(rafRef.current); }, [value, duration]); - return {format(displayValue)}; + // Animation styles + const styles = { + display: 'inline-block', + transition: 'transform 0.4s cubic-bezier(.68,-0.55,.27,1.55), color 0.4s', + transform: animating ? 'scale(1.25) rotate(-5deg)' : 'scale(1)', + color: animating ? (direction === 'up' ? '#22c55e' : direction === 'down' ? '#ef4444' : undefined) : undefined, + fontWeight: animating ? 700 : undefined, + filter: animating ? (direction === 'up' ? 'drop-shadow(0 0 8px #22c55e88)' : direction === 'down' ? 'drop-shadow(0 0 8px #ef444488)' : undefined) : undefined, + }; + + return ( + {format(displayValue)} + ); } export default AnimatedCounter;