如何在React中正确实现加载进度条的动画效果?
React加载进度条3秒内从0到100%的修复方案
问题说明
在React中构建加载进度条时,期望进度条在3秒内从0宽度平滑增长至100%,但当前代码无法实现该预期效果。
原代码问题分析
- CSS过渡与JS定时器冲突:原CSS中给进度条添加了
transition: width 3.05s linear,这会和JS定时器驱动的宽度更新产生冲突,导致动画效果异常。 - 进度计算精度问题:原代码通过定时器间隔10ms累加增量的方式,会因为时间精度误差导致进度无法准确在3秒时到达100%。
- 布局定位缺失:进度条容器
.canvas未设置position: relative,导致绝对定位的进度条布局脱离预期范围。
修复方案
以下提供两种可行的实现方式,按需选择:
方案一:使用requestAnimationFrame精准控制(灵活可控)
通过requestAnimationFrame替代定时器,基于时间差计算进度,保证动画的精准性和流畅度。
修改后的JS代码:
const { useState, useEffect } = React; const Loading = () => { const [isLoading, setIsLoading] = useState(true); const [progress, setProgress] = useState(0); useEffect(() => { const duration = 3000; const startTime = Date.now(); const updateProgress = () => { // 计算已流逝时间占总时长的比例,转换为进度百分比 const elapsed = Date.now() - startTime; const newProgress = Math.min((elapsed / duration) * 100, 100); setProgress(newProgress); // 未完成则继续更新,完成后终止加载状态 if (newProgress < 100) { requestAnimationFrame(updateProgress); } else { setIsLoading(false); } }; const animationFrameId = requestAnimationFrame(updateProgress); // 组件卸载时取消动画帧 return () => cancelAnimationFrame(animationFrameId); }, []); return ( <div className="canvas"> {isLoading && ( <div className="loading_bar" style={{ width: `${progress}%` }} ></div> )} </div> ); }; // 渲染组件 ReactDOM.createRoot( document.getElementById("root") ).render( <Loading /> );
修改后的CSS代码:
/* 给容器添加相对定位,确保进度条在容器内定位 */ .canvas { position: relative; width: 100%; height: 5px; } .loading_bar { position: absolute; top: 0; left: 0; height: 100%; background-color: #c4483f; /* 移除transition,由JS完全控制宽度变化 */ }
HTML代码:
<div id="root"></div> <script src="https://cdnjs.cloudflare.com/ajax/libs/react/18.2.0/umd/react.development.js"></script> <script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/18.2.0/umd/react-dom.development.js"></script>
方案二:纯CSS动画实现(简洁高效)
如果不需要动态调整进度,直接用CSS动画更简洁,无需JS定时器控制。
修改后的JS代码:
const { useState, useEffect } = React; const Loading = () => { const [isLoading, setIsLoading] = useState(true); useEffect(() => { // 3秒后结束加载状态 const timer = setTimeout(() => { setIsLoading(false); }, 3000); return () => clearTimeout(timer); }, []); return ( <div className="canvas"> {isLoading && <div className="loading_bar"></div>} </div> ); }; // 渲染组件 ReactDOM.createRoot( document.getElementById("root") ).render( <Loading /> );
修改后的CSS代码:
.canvas { position: relative; width: 100%; height: 5px; } .loading_bar { position: absolute; top: 0; left: 0; width: 0; height: 100%; background-color: #c4483f; /* 使用CSS动画实现3秒内宽度从0到100% */ animation: loadProgress 3s linear forwards; } @keyframes loadProgress { to { width: 100%; } }
HTML代码同上。
方案对比
- 方案一适合需要中途调整进度、暂停/恢复动画等复杂场景;
- 方案二代码更简洁,性能更优,适合固定时长的简单加载进度需求。
内容的提问来源于stack exchange,提问作者mrconcerned
相关产品推荐
相关产品推荐

