Next.js页面中如何用useEffect实现环形进度条同步动画
Next.js 中环形进度条与数字同步增长的 useEffect 改造方案
问题根源
Next.js 采用服务端渲染(SSR)机制,页面刷新时组件尚未完成客户端 hydration,直接调用 document.querySelector 会无法获取到目标 DOM 元素,导致环形进度条和数字递增动画失效。
改造方案
通过 React 的 useRef + useEffect 组合,确保 DOM 挂载完成后再执行动画逻辑,同时同步进度条与数字的增长节奏:
完整组件代码示例
import { useEffect, useRef } from 'react'; const Widgets = () => { // 用 useRef 保存 DOM 引用,避免重复查询 const progressCircleRef = useRef(null); const numberDisplayRef = useRef(null); // 目标进度值(可根据业务需求调整) const targetProgress = 85; useEffect(() => { // 组件挂载完成后,确保 DOM 已存在 const progressCircle = progressCircleRef.current; const numberDisplay = numberDisplayRef.current; if (!progressCircle || !numberDisplay) return; // 初始化进度条参数 const radius = progressCircle.r.baseVal.value; const circumference = 2 * Math.PI * radius; progressCircle.style.strokeDasharray = `${circumference}`; progressCircle.style.strokeDashoffset = `${circumference}`; let currentProgress = 0; let animationFrameId; // 同步更新进度条与数字的动画函数 const updateAnimation = () => { if (currentProgress >= targetProgress) { cancelAnimationFrame(animationFrameId); return; } currentProgress += 1; // 计算进度条的偏移量 const offset = circumference - (currentProgress / 100) * circumference; progressCircle.style.strokeDashoffset = offset; // 更新显示数字 numberDisplay.textContent = `${currentProgress}%`; animationFrameId = requestAnimationFrame(updateAnimation); }; // 启动动画 animationFrameId = requestAnimationFrame(updateAnimation); // 组件卸载时清理动画帧,避免内存泄漏 return () => { cancelAnimationFrame(animationFrameId); }; }, [targetProgress]); // 依赖目标进度值,值变化时重新触发动画 return ( <div className="widget-container"> <svg className="progress-ring" width="120" height="120"> <circle ref={progressCircleRef} className="progress-ring-circle" stroke="#e0e0e0" strokeWidth="8" fill="transparent" r="46" cx="60" cy="60" /> <circle className="progress-ring-progress" stroke="#4CAF50" strokeWidth="8" strokeLinecap="round" fill="transparent" r="46" cx="60" cy="60" /> </svg> <span ref={numberDisplayRef} className="progress-number">0%</span> </div> ); }; export default Widgets;
关键细节说明
- useRef 替代 document.querySelector:直接绑定到目标DOM元素,确保每次渲染都能拿到正确的节点,避免SSR阶段的DOM查询问题。
- useEffect 执行时机:依赖数组仅包含目标进度值,确保动画逻辑只在组件客户端挂载完成后执行,此时DOM已完全渲染。
- 同步动画逻辑:通过
requestAnimationFrame实现流畅的帧动画,每帧同时更新进度条的stroke-dashoffset属性和数字显示,保证两者同步增长。 - 清理函数:组件卸载时取消动画帧,防止内存泄漏。
内容的提问来源于stack exchange,提问作者iamundertaker
相关产品推荐
相关产品推荐

