I am creating a progress circle bar to use as a timer along with sliders, where each slide will have its own bar
I could achieve it, however I couldn’t synchronize the bars with themselves.
This is what is happening:
As you see it is not working properly. I need the bar to fill 100% of the circle, then proceed to the next dot and repeat the process, however I couldn’t synchronize them.
If I only have one dot it seems to work fine, though:
I am using two setTimeout
, one to decrease the property stroke-dashoffset
of circle
through the controll of percentage
, this one makes the bar to be filled; the second setTimeout
is precisely to make the circle appears around the following dot. I have the variable timer
which controls the interval of the changes and it is in the second setTimeout
. I believe the problem may lie on the time between the two setTimeout
, but it is just a guess.
I am working with hooks, however I couldn’t make it work that way on Codepen, because of that I created a codepen with a class component, but it is even worse, I don’t know why, since it follows the same logics.
https://codepen.io/WegisSilveira/pen/poyPVWq
At any rate, here is my code with hooks. The css is the same as the one on codepen:
import React, { Fragment } from 'react' import './ProgressBar.css' const ProgressBar = props => { let [sqSize, setSqSize] = React.useState(30) let [percentage, setPercentage] = React.useState(0) let [strokeWidth, setStrokeWidth] = React.useState(3) let [trigger, setTrigger] = React.useState(false) let [barIndex, setBarIndex] = React.useState(0) let bars = Array(props.bar).fill(1) let timer = 3000 const barTriggerHandler = () => { setTrigger(!trigger) } if (trigger) { setTimeout(() => { percentage < 99 ? setPercentage(percentage + 1) : setPercentage(0) }, timer / 100); setTimeout(() => { console.log(percentage) barIndex < bars.length - 1 ? setBarIndex(barIndex + 1) : setBarIndex(0) }, timer); } // SVG centers the stroke width on the radius, subtract out so circle fits in square const radius = (sqSize - strokeWidth) / 2; // Enclose cicle in a circumscribing square const viewBox = `0 0 ${sqSize} ${sqSize}`; // Arc length at 100% coverage is the circle circumference const dashArray = radius * Math.PI * 2; // Scale 100% coverage overlay with the actual percent const dashOffset = dashArray - dashArray * percentage / 100; // console.log(dashOffset) return ( <Fragment> { bars.map((bar, i) => { return <svg key={i} width={sqSize} height={sqSize} viewBox={viewBox} onClick={() => barTriggerHandler()} > { i === barIndex ? <Fragment> <circle className="circle-progress" cx={sqSize / 2} cy={sqSize / 2} r={radius} strokeWidth={`${strokeWidth}px`} // Start progress marker at 12 O'Clock transform={`rotate(-90 ${sqSize / 2} ${sqSize / 2})`} style={{ strokeDasharray: dashArray, strokeDashoffset: dashOffset }} /> </Fragment> : null } <circle className="circle-center" cx="50%" cy="50%" r="3" /> </svg> }) } </Fragment> ); } export default ProgressBar
I am using the elements svg
and circle
to create that bar, maybe the problem is here, since until yesterday I didn’t even know these tags and I am a little lost.
If anyone could help me I would appreciate a lot. This is driving me crazy already.
P.S. I took the example to create this bar from this pen: https://codepen.io/bbrady/pen/ozrjKE?editors=1010
Advertisement
Answer
Instead of having two setTimeout
I am using only one. I removed the second one, which was used to add 1
to the index of the array containing the dots and put this step inside the first setTimeout
, now rather then checking the time to make the adition I just check if the circle is 99% filled, if this is the case the circle go to the next dot.
This was the first logic:
if (trigger) { setTimeout(() => { percentage < 99 ? setPercentage(percentage + 1) : setPercentage(0) }, timer / 100); setTimeout(() => { barIndex < bars.length - 1 ? setBarIndex(barIndex + 1) : setBarIndex(0) }, timer); }
Now I’m doing this:
if (trigger) { setTimeout(() => { percentage < 99 ? setPercentage(percentage + 1) : setPercentage(0) if (percentage === 99) { barIndex < bars.length - 1 ? setBarIndex(barIndex + 1) : setBarIndex(0) } }, timer / 100); }
Since both steps are inside the same setTimeout
, there is no longer any interval conflict.