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

React循环进度条实现求助:进度完成后自动重复执行

实现自动循环往复的进度条

我需要一个进度条在完成后自动循环往复,目前只能实现进度条满后重置,但必须手动点击按钮才能重启,无法自动循环。

解决方案:结合React状态与动画结束事件实现自动循环

以下是修改后的代码,核心思路是监听动画结束事件,在进度条完成后自动重启动画,同时保留动态设置时长的功能:

React组件代码

import { useState, useRef, useEffect } from 'react';

export default function Home() {
  const [animationDuration, setAnimationDuration] = useState("0s");
  const [isAnimating, setIsAnimating] = useState(false);
  const [currentDuration, setCurrentDuration] = useState<number>(0);
  const progressRef = useRef<HTMLDivElement>(null);

  const handleTimer = (duration: number) => {
    if (isAnimating) {
      stopTimer();
    } else {
      startTimer(duration);
    }
  };

  const startTimer = (duration: number) => {
    setCurrentDuration(duration);
    setIsAnimating(true);
    setAnimationDuration(`${duration / 1000}s`);
    
    // 强制重绘以重新触发动画
    if (progressRef.current) {
      progressRef.current.classList.remove("animate-growWidth");
      void progressRef.current.offsetWidth;
      progressRef.current.classList.add("animate-growWidth");
    }
  };

  const stopTimer = () => {
    setIsAnimating(false);
    setAnimationDuration("0s");
    progressRef.current?.classList.remove("animate-growWidth");
  };

  // 监听动画结束事件,自动重启循环
  useEffect(() => {
    const handleAnimationEnd = () => {
      if (isAnimating && currentDuration > 0) {
        startTimer(currentDuration);
      }
    };

    const element = progressRef.current;
    element?.addEventListener("animationend", handleAnimationEnd);

    return () => {
      element?.removeEventListener("animationend", handleAnimationEnd);
    };
  }, [isAnimating, currentDuration]);

  return (
    <div>
      <div>
        <div>Timer</div>
        <div style={{ position: "relative", width: "300px", height: "16px", border: "1px solid #ddd", borderRadius: "8px" }}>
          <div
            ref={progressRef}
            className={`absolute h-full bg-slate-300 rounded ${isAnimating ? "animate-growWidth" : ""}`}
            style={{ animationDuration }}
          ></div>
        </div>
        <div style={{ marginTop: "10px" }}>
          <button onClick={() => handleTimer(1500)} style={{ marginRight: "10px" }}>1.5s</button>
          <button onClick={() => handleTimer(5000)}>5s</button>
        </div>
      </div>
    </div>
  );
}

CSS代码

@keyframes growWidth {
  0% {
    width: 0;
  }
  100% {
    width: 100%; /* 使用百分比适配父容器更灵活 */
  }
}

.animate-growWidth {
  animation: growWidth linear forwards;
}

关键修改说明

  • 使用useRef获取进度条DOM元素,用于监听动画事件和强制重绘触发动画重启。
  • 在animationend事件回调中,若当前处于动画状态,自动调用startTimer重启动画,实现循环效果。
  • 重启动画时通过移除/添加动画类+触发重绘的方式,解决CSS动画无法重复触发的问题。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.03 10:07:48