我正在反应中制作计时器进度条动画。我使用
requestAnimationFrame()
来完成此操作。我遇到的问题是,尽管为动画设置了 id,然后在取消时引用该 id(cancelAnimationFrame()
func),但我无法使用 handleStop
停止该动画。为什么?
编辑:嗯,也许它会停止
requestAnimationFrame()
,但它不会停止已经设置的变换动画?
const { useState, useEffect } = React;
const App = () => {
const totalTime = 10000;
const [timeLeft, setTimeLeft] = useState(null);
const [timerId, setTimerId] = useState(null);
useEffect(() => {
setTimeLeft(totalTime);
const start = performance.now();
const animate = (time) => {
const elapsedTime = time - start;
setTimeLeft((prevTimeLeft) => {
if (prevTimeLeft !== null && prevTimeLeft > 0) {
const remainingTime = Math.max(totalTime - elapsedTime, 0);
requestAnimationFrame(animate);
return remainingTime;
}
return 0;
});
};
const id = requestAnimationFrame(animate);
setTimerId(id);
return () => {
cancelAnimationFrame(id);
};
}, []);
const handleStop = () => {
if (timerId) {
cancelAnimationFrame(timerId);
setTimerId(null);
}
};
return (
<div>
<div className="progress-bar">
<div
style={{
transform: `scaleX(${timeLeft ? timeLeft / totalTime : 0})`,
}}
/>
</div>
<button onClick={handleStop}>Pause the animation</button>
</div>
);
};
ReactDOM.render(<App />, document.getElementById("root"));
.progress-bar {
height: 10px;
width: 300px;
background-color: #fdb913;
border-radius: 10px;
overflow: hidden;
margin-top: 2rem;
margin-bottom: 3rem;
}
.progress-bar div {
background-color: grey;
height: 100%;
transform-origin: left center;
}
<div id="root">
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/18.1.0/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/18.1.0/umd/react-dom.production.min.js"></script>
</div>
也许你可以尝试这些步骤。
setState()
回调函数中调用requestAnimationFrame。useRef()
而不是 useState()
来管理计时器 ID。我用这些步骤编写了代码,它在这个代码沙箱中运行良好。
const timerRef = useRef(null);
useEffect(() => {
setTimeLeft(totalTime);
const start = performance.now();
const animate = (time) => {
const elapsedTime = time - start;
setTimeLeft((prevTimeLeft) => {
if (prevTimeLeft !== null && prevTimeLeft > 0) {
const remainingTime = Math.max(totalTime - elapsedTime, 0);
return remainingTime;
}
return 0;
});
timerRef.current = requestAnimationFrame(animate);
};
timerRef.current = requestAnimationFrame(animate);
return () => {
if (timerRef.current !== null) {
cancelAnimationFrame(timerRef.current);
timerRef.current = null;
}
};
}, []);