如何控制React动画计数器速度?实现多计数器同步完成动画
解决多计数器同步动画的问题
原方案的核心问题是依赖setTimeout逐次累加数值,这种方式会因为浏览器定时器精度(最小延迟通常为4ms)、React渲染周期的叠加误差,导致动画时长不稳定,多个计数器无法同步完成。改用基于时间差的进度计算+requestAnimationFrame可以彻底解决这个问题,让所有计数器严格在指定时长内完成动画。
修改后的实现代码
import { useState, useEffect, useRef } from 'react'; const Counter = ({ count, speed }) => { const [counter, setCounter] = useState(0); const startTimeRef = useRef(null); const animationIdRef = useRef(null); // 动画核心逻辑:基于时间差计算当前进度 const animateCounter = () => { const elapsedTime = Date.now() - startTimeRef.current; const progress = Math.min(elapsedTime / speed, 1); // 进度控制在0-1之间 const currentValue = Math.floor(progress * count); setCounter(currentValue); // 未完成则继续下一帧 if (progress < 1) { animationIdRef.current = requestAnimationFrame(animateCounter); } }; useEffect(() => { // 重置计数器 setCounter(0); // 清除之前的动画 if (animationIdRef.current) { cancelAnimationFrame(animationIdRef.current); } // 启动新动画 startTimeRef.current = Date.now(); animationIdRef.current = requestAnimationFrame(animateCounter); // 组件卸载或依赖变化时清理动画 return () => { if (animationIdRef.current) { cancelAnimationFrame(animationIdRef.current); } }; }, [count, speed]); return ( <div className="counter"> {Intl.NumberFormat('en-EN').format(counter)} </div> ); };
关键逻辑说明
- requestAnimationFrame:浏览器专门为动画优化的API,会与页面渲染帧同步执行,避免定时器的精度误差,同时减少不必要的重渲染。
- 时间差计算进度:通过记录动画开始时间,计算已流逝时间占总时长
speed的比例,直接算出当前应该显示的数值,而非逐次累加。这种方式不管目标数值count多大,都能严格在speed时长内完成动画,多个计数器的进度完全同步。 - 依赖清理:在组件卸载或
count/speed变化时,清除之前的动画帧,避免内存泄漏和残留动画。
边界情况处理
- 如果
count为0,直接显示0,不会触发动画。 - 如果
speed为0,会直接跳转到目标数值(可根据需求添加判断,比如speed为0时直接设置counter为count)。
内容的提问来源于stack exchange,提问作者Arkellys
相关产品推荐
相关产品推荐

