如何在React中实现两端可连续循环移动的antD进度条?
解决方案
问题根因
你写的两套代码失效的核心原因是React闭包陷阱:定时器和requestAnimationFrame回调中拿到的loadingPercent是当前渲染帧的快照,不会随着状态更新实时变化,导致数值累加逻辑异常。
具体实现方案
方案1:直接使用antd Progress原生能力(最简单)
antd的Progress组件自带不确定进度模式,开启indeterminate属性即可自动实现起点终点同时移动、循环滚动的效果,完全不需要自己写定时器逻辑:
{loading && <Progress showInfo={false} indeterminate size="small" />}
该模式会自动在loading为true时启动循环动画,loading为false时停止,完全符合你的需求。
方案2:自定义实现逻辑(需要控制动画细节时用)
如果需要自定义进度条移动速度、区间长度等参数,按如下步骤实现:
1. 解决闭包问题
使用setPercent的函数式更新语法,直接取上一轮的状态值做计算,避免闭包干扰;同时用useRef存储requestAnimationFrame的id,方便卸载时清除。
2. 实现起点移动效果
通过自定义CSS修改进度条填充块的样式,配合维护的进度区间值实现两端同时移动。
完整代码示例:
import { useState, useEffect, useRef } from 'react'; import { Progress } from 'antd'; const CustomProgress = ({ loading }) => { const [percent, setPercent] = useState(0); const rafIdRef = useRef(null); const animate = () => { setPercent(prev => { // 进度到100后重置为0,实现循环 if (prev >= 100) return 0; // 控制移动速度,数值越大速度越快 return prev + 0.5; }); rafIdRef.current = requestAnimationFrame(animate); }; useEffect(() => { if (loading) { // loading为true时启动动画 rafIdRef.current = requestAnimationFrame(animate); } else { // loading为false时停止动画,重置进度 cancelAnimationFrame(rafIdRef.current); setPercent(0); } return () => cancelAnimationFrame(rafIdRef.current); }, [loading]); return ( <div className="custom-progress-wrap"> <Progress showInfo={false} percent={percent} size="small" /> </div> ); };
/* 自定义进度条样式,实现起点移动效果 */ .custom-progress-wrap .ant-progress-bg { /* 控制进度块的长度,30%表示进度块占总进度条的30% */ width: 30% !important; /* 用left属性控制进度块的位置,和percent绑定实现两端移动 */ left: calc(var(--ant-progress-percent) * 1%) !important; transform: translateX(-100%) !important; transition: none !important; }
问题直接解答
- 如何实现进度条起点的移动:通过自定义CSS修改进度条填充块的
left属性,和你维护的进度值绑定,代替原生固定在0的起点逻辑 - 如何让进度条滚动效果循环连续:使用函数式更新状态避免闭包问题,进度值到100时自动重置为0,配合requestAnimationFrame实现平滑连续动画
内容的提问来源于stack exchange,提问作者devb
相关产品推荐
相关产品推荐

