React如何实现3个数字组件2秒递增动画同时结束(无第三方库)
数字同步递增动画实现方案
需求回顾
- 3个接收不同目标数值的组件,数字递增动画总时长固定为2秒
- 所有组件的动画需在同一时间点结束
- 实现过程不引入第三方库
原有代码的问题
- 采用「固定步长+1 + setInterval定时触发」的计数逻辑,不同目标值的总执行步数差异极大:目标值100的组件执行100次就结束,目标值700的组件需要执行700次,完全无法保证同时结束
setInterval传入0.01ms延迟属于无效参数,浏览器遵循W3C规范,嵌套定时器的最小延迟为4ms,小于该值的参数会被强制修正,根本达不到预期执行频率- 计数逻辑和真实时间完全脱钩,受浏览器事件循环调度、标签页后台节流、主线程长任务阻塞影响,实际运行时长偏差极大,无法稳定保证2秒的固定时长
核心实现要点
- 动画逻辑必须以时间进度为核心计算依据,而非固定计数步长:记录动画启动的起始时间戳,每帧计算当前已消耗时长,通过「已消耗时长/总动画时长」得到动画完成百分比,再乘以目标值得到当前帧应该显示的数字
- 用
requestAnimationFrame做帧调度替代setInterval:它和浏览器屏幕刷新率同步,执行时机更稳定,动画更流畅,不会出现定时器排队导致的跳帧、卡顿问题 - 时间计算优先使用
performance.now()获取高精度时间戳,相比Date.now()不受系统时间修改影响,进度计算更准确 - 必须做进度边界兜底:当计算出的动画进度超过100%时,直接将显示值设为最终目标值,终止帧调度,避免数值超出目标、动画超时不结束的问题
- 组件卸载时要清理未执行完的动画帧任务,避免内存泄漏
- 所有组件统一使用固定2秒的时长参数,只要组件在同一时间点挂载启动动画,自然会同步结束,不需要额外引入全局状态做同步控制
修正后的可运行代码
const {useState, useEffect, useRef} = React; const AnimationNumber = ({ number, duration = 2000 }) => { const [displayNum, setDisplayNum] = useState(0); const animationTaskRef = useRef(null); useEffect(() => { // 记录动画启动的高精度起始时间 const startTime = performance.now(); const frameUpdate = () => { const elapsedTime = performance.now() - startTime; // 计算当前进度,最大不超过100% const progress = Math.min(elapsedTime / duration, 1); // 计算当前显示值,取整避免出现小数 const currentValue = Math.floor(progress * number); setDisplayNum(currentValue); if (progress < 1) { animationTaskRef.current = requestAnimationFrame(frameUpdate); } }; // 启动第一帧动画 animationTaskRef.current = requestAnimationFrame(frameUpdate); // 组件卸载时清理动画任务 return () => { if (animationTaskRef.current) { cancelAnimationFrame(animationTaskRef.current); } }; }, [number, duration]); return ( <div data-target={number}> <strong> <span>{displayNum}</span> </strong> </div> ); }; function Example() { return ( <div> <AnimationNumber number={700} /> <AnimationNumber number={100} /> <AnimationNumber number={470} /> </div> ); } ReactDOM.render(<Example />, document.getElementById('root'));
内容的提问来源于stack exchange,提问作者horoyoi o
相关产品推荐
相关产品推荐

