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

React实现多数值差异化增速且同步停止的自动递增效果

实现同步停止的数字递增器

问题

我需要实现一个自动数字递增器,要求不同数值根据自身大小以不同速度增长,最终所有数值同步停止。我尝试用React的useEffect、useState和setInterval实现,但各个数值的停止时间差距很大,以下是我的代码:

我的代码

React 代码

import React from "react";
import classes from "./Numbers.module.css";

function Numbers() {
  const [counter, setCounter] = React.useState(0);
  React.useEffect(() => {
    const interval = setInterval(() => {
      if (counter < 1839) {
        setCounter(counter + 1);
      }
    }, 5);

    return () => {
      clearInterval(interval);
    };
  });
  const [counter1, setCounter1] = React.useState(0);
  React.useEffect(() => {
    const interval = setInterval(() => {
      if (counter1 < 512) {
        setCounter1(counter1 + 1);
      }
    }, 5);

    return () => {
      clearInterval(interval);
    };
  });
  const [counter2, setCounter2] = React.useState(0);
  React.useEffect(() => {
    const interval = setInterval(() => {
      if (counter2 < 729) {
        setCounter2(counter2 + 1);
      }
    }, 5);

    return () => {
      clearInterval(interval);
    };
  });
  const [counter3, setCounter3] = React.useState(0);
  React.useEffect(() => {
    const interval = setInterval(() => {
      if (counter3 < 2729) {
        setCounter3(counter3 + 1);
      }
    }, 5);

    return () => {
      clearInterval(interval);
    };
  });
  return (
    <div class={classes["number-section"]}>
      <h1 class={classes["Number-heading"]}>Numbers expose our character.</h1>
      <div class={classes["notes-books-pyq"]}>
        <div>
          <span class={`${classes["notes-number"]} ${classes["number"]}`}>
            {counter}+
          </span>
          <br />
          <h2 class={`${classes["notes-label"]} ${classes.label}`}>Notes</h2>
        </div>
        <div>
          <span class={`${classes["books-number"]} ${classes.number}`}>
            {counter1}+
          </span>
          <br />
          <h2 class={`${classes["books-label"]} ${classes.label}`}>Books</h2>
        </div>
        <div>
          <span class={`${classes["pyqs-number"]} ${classes.number}`}>
            {counter2}+
          </span>
          <br />
          <h2 class={`${classes["pyqs-label"]} ${classes.label}`}>PYQ's</h2>
        </div>
        <div>
          <span class={`${classes["downloads-number"]} ${classes.number}`}>
            {counter3}+
          </span>
          <br />
          <h2 class={`${classes["downloads-label"]} ${classes.label}`}>
            Download's
          </h2>
        </div>
      </div>
    </div>
  );
}
export default Numbers;

CSS 代码

.number-section {
  background-color: #efeef9;
  padding: 4rem 6rem;
  border-radius: 2rem;
  width: 120rem;
  margin: 10rem auto;

  display: flex;
  justify-content: space-between;
  align-items: center;
}

.notes-books-pyq {
  display: flex;
  justify-content: space-between;
  align-items: center;
  gap: 8.6rem;
}

.notes-books-pyq div {
  display: flex;
  flex-direction: column;
  justify-content: flex-end;
  align-items: center;
}

.Number-heading {
  font-size: 3rem;
  color: #333;
}

.number {
  font-size: 4.4rem;
  color: #5236ff;
  font-weight: 600;
}

.label {
  font-size: 2rem;
  color: #333;
}

解决方案

你的问题根源在于:

  1. 每个计数器独立使用setInterval且步长固定为1,数值越大需要的时间越长,无法同步停止
  2. useEffect未设置依赖项,组件每次渲染都会重新创建定时器,引发无效执行
  3. 直接用counter + 1更新状态,会因闭包问题拿到旧状态值,导致更新延迟

要实现同步停止,核心思路是基于总时长计算统一进度,所有计数器共享同一个定时器,根据当前时间占总时长的比例计算实时数值。

优化后的React代码

import React, { useState, useEffect } from "react";
import classes from "./Numbers.module.css";

function Numbers() {
  // 定义各项目标数值
  const targets = {
    notes: 1839,
    books: 512,
    pyqs: 729,
    downloads: 2729
  };

  // 存储当前显示的所有数值
  const [currentValues, setCurrentValues] = useState({
    notes: 0,
    books: 0,
    pyqs: 0,
    downloads: 0
  });

  // 动画总时长(可按需调整,单位:毫秒)
  const totalDuration = 2000;

  useEffect(() => {
    const startTime = Date.now();
    const interval = setInterval(() => {
      const elapsedTime = Date.now() - startTime;
      // 计算进度比例,最大限制为1(即100%)
      const progress = Math.min(elapsedTime / totalDuration, 1);

      // 根据进度更新每个数值
      const updatedValues = Object.entries(targets).reduce((acc, [key, target]) => {
        acc[key] = Math.floor(target * progress);
        return acc;
      }, {});

      setCurrentValues(updatedValues);

      // 进度完成后清除定时器
      if (progress === 1) {
        clearInterval(interval);
      }
    }, 16); // 约60fps,保证动画流畅且性能合理

    return () => clearInterval(interval);
  }, []); // 空依赖数组,仅在组件挂载时执行一次

  return (
    <div className={classes["number-section"]}>
      <h1 className={classes["Number-heading"]}>Numbers expose our character.</h1>
      <div className={classes["notes-books-pyq"]}>
        <div>
          <span className={`${classes["notes-number"]} ${classes["number"]}`}>
            {currentValues.notes}+
          </span>
          <br />
          <h2 className={`${classes["notes-label"]} ${classes.label}`}>Notes</h2>
        </div>
        <div>
          <span className={`${classes["books-number"]} ${classes.number}`}>
            {currentValues.books}+
          </span>
          <br />
          <h2 className={`${classes["books-label"]} ${classes.label}`}>Books</h2>
        </div>
        <div>
          <span className={`${classes["pyqs-number"]} ${classes.number}`}>
            {currentValues.pyqs}+
          </span>
          <br />
          <h2 className={`${classes["pyqs-label"]} ${classes.label}`}>PYQ's</h2>
        </div>
        <div>
          <span className={`${classes["downloads-number"]} ${classes.number}`}>
            {currentValues.downloads}+
          </span>
          <br />
          <h2 className={`${classes["downloads-label"]} ${classes.label}`}>
            Download's
          </h2>
        </div>
      </div>
    </div>
  );
}

export default Numbers;

关键优化点

  • 统一定时器:只用一个定时器驱动所有数值更新,从根源保证同步性
  • 进度比例计算:所有数值基于同一进度比例增长,最终必然同步停止
  • 修复闭包问题:直接基于目标值和进度计算当前数值,避免旧状态闭包影响
  • 流畅帧率设置:采用16ms的间隔(接近60帧每秒),平衡动画流畅度与性能
  • 规范React语法:将class替换为React要求的className,避免DOM属性错误
  • 精简状态管理:用单个对象存储所有当前数值,减少重复的state和useEffect逻辑

内容的提问来源于stack exchange,提问作者Shivam kaushal

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.21 12:20:06