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.