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; }
解决方案
你的问题根源在于:
- 每个计数器独立使用setInterval且步长固定为1,数值越大需要的时间越长,无法同步停止
- useEffect未设置依赖项,组件每次渲染都会重新创建定时器,引发无效执行
- 直接用
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
相关产品推荐
相关产品推荐

