I created a simple reproduction of my code. Im having an issue with managing an animation of progress bar. The issue is that after pausing an animation you can see the animation goes a little bit backwards and is not being paused in the place it should be when clicking a button. Where is the issue in my code?
And worth mentioning is that its only a reproduction and would be good if class removal is not touched cause in the real code i need to restart the animation on certain condition.
const { useState, useEffect } = React;
const App = () => {
const totalTime = 10;
const [timeLeft, setTimeLeft] = useState(totalTime);
const [isTimerAnimated, setIsTimerAnimated] = useState(true);
useEffect(() => {
const interval = setInterval(() => {
if (timeLeft > 0 && isTimerAnimated) {
setTimeLeft((prevTimeLeft) => prevTimeLeft - 1);
}
}, 1000);
return () => clearInterval(interval);
}, [timeLeft, isTimerAnimated]);
const handleAnimationState = () => {
if (isTimerAnimated) {
setIsTimerAnimated(false);
} else {
setIsTimerAnimated(true);
setTimeLeft(totalTime);
}
};
return (
<div>
<div className="progress-bar">
<div
className={`${isTimerAnimated ? "animated" : ""}`}
style={{
transform: `scaleX(${timeLeft ? timeLeft / totalTime : 0})`,
animationDuration: `${totalTime}s`,
}}
/>
</div>
<button onClick={handleAnimationState}>{isTimerAnimated ? "Pause the animation" : "Restart the animation"}</button>
</div>
);
};
ReactDOM.render(<App />, document.getElementById("root"));
@keyframes timerAnimation {
from {
transform: scaleX(1);
}
to {
transform: scaleX(0);
}
}
.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%;
animation-play-state: paused;
animation-fill-mode: forwards;
transform-origin: left center;
}
.progress-bar div.animated {
animation: timerAnimation linear;
animation-play-state: running;
}
<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>
2
Answers
I have changed the timeLeft state to track milliseconds (totalTime * 1000) instead of seconds
I have changed the interval to run every 100 milliseconds (setInterval callback) instead of every second enhances the responsiveness of your progress bar.
You can do something like this, instead of scaling it, you can do its width, which you know will always be between 0 am 100.
Then count your time in milliseconds. So, You can make tiny changes to the progress instead of steps. Then map your time in milliseconds to the progress