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
相关产品推荐
相关产品推荐

