React JS progress circle bar is a simple and effective way to visualize

Currently, I am in the process of developing a progress circle bar that will function as a timer alongside sliders. Each slide is intended to have its own corresponding progress bar.

While I have managed to create the bars individually, I am facing challenges with synchronizing them effectively.

The issue is clearly illustrated by the following GIF: https://i.sstatic.net/NMvSA.gif

As evident from the demonstration, the synchronization is not functioning as intended. My goal is for each bar to fill 100% of the circle, then transition to the next dot and repeat the cycle seamlessly. However, achieving this seamless synchronization has proven to be quite difficult.

In scenarios where only one dot is present, the functionality appears to work correctly, as shown here: https://i.sstatic.net/lwloH.gif

To provide some insight into my approach, I am utilizing two setTimeout functions. One is responsible for decreasing the 'stroke-dashoffset' property of the 'circle' element through the manipulation of 'percentage', thereby filling the bar. The second setTimeout function is specifically designed to display the circle surrounding the subsequent dot. A variable named 'timer' regulates the timing of these changes and is embedded within the second setTimeout function. While I suspect that the issue may stem from the time interval between the two setTimeout functions, this is merely speculation on my part.

Although I initially attempted to implement hooks, I encountered difficulties when attempting to replicate the functionality on Codepen. Consequently, I resorted to creating a code snippet featuring a class component; however, the performance was even more erratic than expected. The underlying reasons for this discrepancy remain unclear to me, particularly since both implementations adhere to similar principles.

For reference, you can view the class component code snippet on Codepen via the following link: https://codepen.io/WegisSilveira/pen/poyPVWq.

Subsequently, provided below is my code incorporating hooks. The CSS remains consistent with the styling utilized in the aforementioned Codepen sample:

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

Given that the progress circle embellishment relies heavily on the utilization of 'svg' and 'circle' elements, it is plausible that the problem stems from this particular setup. Prior to embarking on this endeavor, I had limited familiarity with these tags, which has inevitably contributed to my current predicament.

If anyone possesses the expertise necessary to assist me in resolving this dilemma, your guidance would be immensely appreciated. At this juncture, this challenge is causing considerable frustration, and any assistance rendered would be invaluable.

P.S. The inspiration for creating this form of progress bar was drawn from the pen available at the following link: https://codepen.io/bbrady/pen/ozrjKE?editors=1010

Answer №1

By consolidating two separate setTimeout functions into one, I have streamlined the process. The unnecessary second timeout, which previously incremented a value in an array of dots, has been eliminated. Now, within the single setTimeout, I increment the percentage filled in the circle and check if it has reached 99%. If so, the circle moves to the next dot.

Initially, the logic looked like this:

if (trigger) {
    setTimeout(() => {
        percentage < 99 ? setPercentage(percentage + 1) : setPercentage(0)
    }, timer / 100);

    setTimeout(() => {
        barIndex < bars.length - 1 ? setBarIndex(barIndex + 1) : setBarIndex(0)
    }, timer);
    
}

The revised approach is as follows:

if (trigger) {
    setTimeout(() => {
        percentage < 99 ? setPercentage(percentage + 1) : setPercentage(0)

        if (percentage === 99) {
             barIndex < bars.length - 1 ? setBarIndex(barIndex + 1) : setBarIndex(0)
        }

    }, timer / 100);        
}

Combining both steps within the same setTimeout resolves any interval conflicts that may have arisen previously.

Similar questions

If you have not found the answer to your question or you are interested in this topic, then look at other similar questions below or use the search

Checking validation with parsley.js without triggering form submission

I have been working with the latest release of Parsley for data validation. While it is validating my data correctly, I am encountering an issue where the form does not submit after validation is complete. I have spent hours trying to troubleshoot this pro ...

What steps can be taken to ensure that a box is the exact same size

Is it possible to make two of my main buttons the same size? I'm just starting out in web development and could use some guidance. Below is the HTML and CSS code I currently have for my index.html: <body> <div> <img src="img/projec ...

Discovering identical objects in two arrays in Angular using TypeScript is a breeze

I've hit a roadblock with a TypeScript problem in my Angular service. I have an array of ingredients: private ingredients: Ingredient[] = [ new Ingredient('farina', 500), new Ingredient('burro', 80), new Ingredient('ucc ...

Is it possible to make asynchronous calls to an external API without causing any drag on the overall speed of the website?

Currently, I operate an online store where at the end of the page, I integrate the eBay API with items related to the main product. Unfortunately, I have noticed that this integration causes a significant delay in the overall page loading by 4 to 10 secon ...

The .NET controller does not receive traffic for the GET method

Having some trouble populating a table with JSON data using angular ng-repeat. No errors are showing up in the console, and my breakpoint in the .NET Controller isn't being triggered. Here's the code for the App Controller: var app = angular.mo ...

Encountering package.json with missing dependencies while running the command 'create-react-app my-app'

After attempting to utilize both npm and npx, I repeatedly encountered an error related to missing package dependencies. https://i.sstatic.net/fJVhk.png ...

What is the best way to transfer information from node.js to HTML?

This is a sample code in Node.js var http = require('http'); var request = require("request"); http.createServer(function (req, res) { res.writeHead(200, {'Content-Type': 'text/html'}); res.end('Hello World!& ...

The layout of my website appears distorted on mobile devices

When I view my website, http://www.healthot.com, on an iPhone or other mobile device, the page doesn't display correctly. It scales the window, requiring me to zoom out to see the entire page. To better understand the issue, please refer to this photo ...

Restoring gitignore files: A step-by-step guide

After downloading a zipped file from github, I realized that some features were not working due to a gitignore in the file. Is there a way to extract data from the gitignore file in order to make all features of this app function properly? It seems like t ...

Is there a way to include multiple values within a tag in ReactJs?

I need assistance with retrieving the value of each option tag in my Component that consists of select and option tags. How can I achieve this when onChange event occurs? Any suggestions would be greatly appreciated! const HandleChange = (e) => { ...

PyQt TreeView scrollbar obstructing the header

I've been experimenting with the QTreeView widget (instead of QListView) in order to have a horizontal header. Additionally, I wanted to use stylesheets to customize the colors and appearance of the header and scrollbars. However, there is an issue w ...

What is the best way to deactivate buttons with AngularJS?

I have a situation where I need to disable the save button in one function and permanently disable both the save as draft and save buttons in another function using AngularJS. How can I accomplish this task with the disable functionality in AngularJS? Her ...

Navigating through child elements within a div using JavaScript

I recently coded a div using this snippet... let sidebarBox = document.createElement("div"); sidebarBox.id = "sidebarBox"; ...and then I created a second div like so... let sidebarAd = document.createElement("div"); sidebarAd.className = "sidebarAd"; B ...

How can I incorporate sublists into my Note application using ReactJS?

Just starting out with ReactJS and I have a project where I need to create an app similar to Notes. Users should be able to add sublists to their notes, with each note being saved in the state as a subarray within an object. The desired state structure is ...

Securing URL Query Parameters

Working with Liferay 5.2 and ExtJS 3.4 poses a challenge for parameter passing in the URL. The issue arises when reports are generated based on parameters passed in the URL, allowing manual changes that lead to the generation of unauthorized reports. The ...

Is there a way to allow only the block code to shift while keeping the other span tags stationary?

Is it possible to change the text without affecting other span tags in the code? I want to make sure only this specific text is updated. How can I achieve that? var para_values = [ { content: "BRAND " }, { content: "MISSION" } ]; functi ...

Tips on creating an inline editable cell in a Tree View

I've been working on a category tree that includes expand and collapse buttons. You can see what I have so far here: Category Tree Now, I'm looking to make each item inline editable. Can someone guide me on how to achieve this? If you want to t ...

What is the process for creating a personalized <a> tag using CSS?

On my website, I am looking to differentiate between two types of <a> tags - one for inline links and the other for standalone links. This means that I want these two variations of <a> tags to have distinct colors and styles. For inline links, ...

Can I combine the col and d-flex classes in Bootstrap 5 without breaking any rules?

Is it possible to use both col and d-flex classes together? I want to center the element with the class "description" by combining them. <section class="container-fluid mb-5"> <div class="row"> <div class=&q ...

regular expression for replacing only alphanumeric strings

I'm currently developing a tool that tags alphanumeric words based on the option selected from the right-click context menu. However, I am facing issues when a group of words containing special characters is selected. I have been using the following ...