You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何控制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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.22 14:24:18