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

React中CSS Motion Path点击按钮重复触发动画失效问题求助

你的问题在于,第一次点击按钮时,PathStyled组件被渲染出来,CSS动画会自动播放;但之后再次点击按钮时,isStartAnime已经是true了,组件不会重新挂载,CSS动画不会自动重复播放——CSS动画默认只在元素首次添加到DOM时触发一次。

下面给你两种可行的解决方案,按需选择:


方案1:用key属性强制组件重新挂载

这是最简单直接的方法,每次点击"Start"按钮时,更新一个唯一的key值,React会认为这是一个新的组件实例,从而重新挂载它,动画就会重新播放。

修改后的完整代码:

import React, { useState } from "react";
import styled from "styled-components";
import "./App.css";

const PathStyled = styled.div`
  width: 50px;
  height: 50px;
  border: 1px solid hsl(343, 100%, 58%, 0.3);
  border-right: 5px solid hsl(343, 100%, 58%);
  background: hsla(343, 100%, 58%, 0.3) radial-gradient(
    circle,
    hsl(343, 100%, 58%, 1) 3px,
    hsl(343, 100%, 58%, 0) 3px
  );
  offset-path: ${(props) => {
    const pathStr = "path('M40,100 l" + props.x + ",0')";
    console.log(pathStr);
    return pathStr;
  }};
  animation: distance 4000ms ease-in-out;
`;

function App() {
  const PATH_X_SEG = 200;
  const PATH_Y_SEG = 0;
  const [path, setPath] = useState({ x: 200, y: 0 });
  const [isStartAnime, setIsStartAnime] = useState(false);
  // 添加动画key状态,用于强制组件重新挂载
  const [animationKey, setAnimationKey] = useState(0);

  const addToSVG = () => {
    setPath({ x: path.x + PATH_X_SEG, y: path.y + PATH_Y_SEG });
  };

  const startAnimation = () => {
    // 更新key值,让React识别为新组件
    setAnimationKey(prev => prev + 1);
    setIsStartAnime(true);
  };

  return (
    <div className="App">
      <div className="controls">
        <button onClick={addToSVG}>Add segment to SVG</button>
        <button onClick={startAnimation}>Start</button>
      </div>
      <br />
      <div className="section-style">
        <svg viewBox="0 0 700 700">
          <path d={`M40,100 l${path.x},${path.y}`} fill="none" stroke="lightgrey" />
        </svg>
        {isStartAnime ? <PathStyled key={animationKey} x={path.x} y={path.y} /> : ''}
      </div>
    </div>
  );
}

export default App;

方案2:通过控制CSS动画属性触发播放

如果不想重新挂载组件,也可以通过动态切换动画属性来触发重复播放。核心思路是先移除动画,再重新添加动画,利用状态更新的异步性完成重置。

修改后的完整代码:

import React, { useState } from "react";
import styled from "styled-components";
import "./App.css";

const PathStyled = styled.div`
  width: 50px;
  height: 50px;
  border: 1px solid hsl(343, 100%, 58%, 0.3);
  border-right: 5px solid hsl(343, 100%, 58%);
  background: hsla(343, 100%, 58%, 0.3) radial-gradient(
    circle,
    hsl(343, 100%, 58%, 1) 3px,
    hsl(343, 100%, 58%, 0) 3px
  );
  offset-path: ${(props) => {
    const pathStr = "path('M40,100 l" + props.x + ",0')";
    console.log(pathStr);
    return pathStr;
  }};
  // 根据props动态控制动画是否生效
  animation: ${props => props.isPlaying ? 'distance 4000ms ease-in-out' : 'none'};
`;

function App() {
  const PATH_X_SEG = 200;
  const PATH_Y_SEG = 0;
  const [path, setPath] = useState({ x: 200, y: 0 });
  const [isPlaying, setIsPlaying] = useState(false);

  const addToSVG = () => {
    setPath({ x: path.x + PATH_X_SEG, y: path.y + PATH_Y_SEG });
  };

  const startAnimation = () => {
    // 先停止动画,延迟后重新启动,确保React完成状态更新
    setIsPlaying(false);
    setTimeout(() => {
      setIsPlaying(true);
    }, 10);
  };

  return (
    <div className="App">
      <div className="controls">
        <button onClick={addToSVG}>Add segment to SVG</button>
        <button onClick={startAnimation}>Start</button>
      </div>
      <br />
      <div className="section-style">
        <svg viewBox="0 0 700 700">
          <path d={`M40,100 l${path.x},${path.y}`} fill="none" stroke="lightgrey" />
        </svg>
        <PathStyled isPlaying={isPlaying} x={path.x} y={path.y} />
      </div>
    </div>
  );
}

export default App;

这两种方案都能解决你遇到的问题,方案1更适合简单场景,方案2则保留了组件的DOM状态,适合需要在动画过程中保持组件存在的场景。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.05.07 15:13:13