Search
Close this search box.
Search
Close this search box.

Creating a Curved Progress Bar Using JavaScript

Curved Progress Bar

Table of Contents

The Curved Progress Bar is the primary tool on the EdTech website used to display the modules’ progress. It is employed to show the user’s progress.

There are two primary sorts of progress bars: circular ones can give your web projects more flair and distinctiveness, while linear progress bars are more conventional.

This post will show you how to use HTML, CSS, and JavaScript to design a circular progress bar from scratch.

gif

Both beginner and experienced web developers will find this article to be extremely helpful. To make the progress bar easy to grasp for everyone, we’ll design it step by step. By the time you finish reading this article, you’ll know how to develop your own progress bar.

Required conditions:

Make sure you have the required resources and knowledge before beginning:

  1. a basic understanding of JavaScript, HTML, and CSS.
  2. Your preferred code editor (such as Atom, Sublime Text, or Visual Studio Code).
  3. A web browser is required in order to test your progress bar.

Curved Progress Bar (Source Code)

You must produce three files in order to obtain the HTML, CSS, and JavaScript code needed to construct a curving progress bar: an HTML file, a CSS file, and a JavaScript file. You can copy and paste the following code into your documents after creating these three files. By selecting the download option, you can also get the source code files in the full version.

HTML Sturcture

Let’s begin by putting our circular progress bar’s HTML structure together. Make a new HTML file and add the code that follows:

				
					<div class="wrapper">
  <div class="container chart" data-size="300" data-value="73" data-arrow="down"></div>
</div>
				
			

We have designed a two-element, basic framework. The “wrapper” class on the first div is used as a container. There is another div with the classes “container” and “chart” inside this container. Custom data attributes like “data-size” (set to 300), “data-value” (set to 73), and “data-arrow” (indicating “down”) are contained in this second div. The size, value, and directional indicator of the chart elements, among other details, frequently appear in these custom data properties.

CSS styling

Let’s now apply CSS styling to our circular progress bar. Add the following styles to a file called Styles.css:

				
					body {
  background-color: #18222f;
}
.wrapper {
  position: absolute;
  width: 400px;
  height: 400px;
  margin: auto;
  top: 0;
  left: 0;
  bottom: 0;
  right: 0;
  display: flex;
}
.container {
  font: 1px;
  padding: 0 20px;
}
				
			

For a basic design, we’ll begin by setting the page’s background color to a deep navy-gray shade (#18222f). Next, we’ll apply styles to our container element by choosing the class “.wrapper”. We’ll set its size to 400×400 pixels, and we’ll use the flex display, justify-content, and align-items properties to center it both vertically and horizontally within its parent container. In addition, we’ll add a 20-pixel horizontal padding and adjust the progress bar’s font size to 1 pixel, essentially obscuring the text content, using the “.container” class.

CSS Output:

Adding JavaScript code to Add Functionality

In order to make our circular progress bar interactive, let’s now add some JavaScript code. Make a script.js file and insert the following code in it:

				
					class Dial {
  constructor(container) {
    this.container = container;
    this.size = this.container.dataset.size;
    this.strokeWidth = this.size / 8;
    this.radius = this.size / 2 - this.strokeWidth / 2;
    this.value = this.container.dataset.value;
    this.direction = this.container.dataset.arrow;
    this.svg;
    this.defs;
    this.slice;
    this.overlay;
    this.text;
    this.arrow;
    this.create();
  }

  create() {
    this.createSvg();
    this.createDefs();
    this.createSlice();
    this.createOverlay();
    this.createText();
    this.createArrow();
    this.container.appendChild(this.svg);
  }

  createSvg() {
    let svg = document.createElementNS("http://www.w3.org/2000/svg", "svg");
    svg.setAttribute("width", `${this.size}px`);
    svg.setAttribute("height", `${this.size}px`);
    this.svg = svg;
  }

  createDefs() {
    var defs = document.createElementNS("http://www.w3.org/2000/svg", "defs"),
      linearGradient = document.createElementNS(
        "http://www.w3.org/2000/svg",
        "linearGradient"
      ),
      stop1 = document.createElementNS("http://www.w3.org/2000/svg", "stop"),
      stop2 = document.createElementNS("http://www.w3.org/2000/svg", "stop"),
      linearGradientBackground = document.createElementNS(
        "http://www.w3.org/2000/svg",
        "background"
      );
    linearGradient.setAttribute("id", "gradient");
    stop1.setAttribute("stop-color", "#ffa000");
    stop1.setAttribute("offset", "0%");
    linearGradient.appendChild(stop1);
    stop2.setAttribute("stop-color", "#f25767");
    stop2.setAttribute("offset", "100%");
    linearGradient.appendChild(stop2);
    linearGradientBackground.setAttribute("id", "gradient-background");
    var stop1 = document.createElementNS("http://www.w3.org/2000/svg", "stop");
    stop1.setAttribute("stop-color", "rgba(0,0,0,0.2)");
    stop1.setAttribute("offset", "0%");
    linearGradientBackground.appendChild(stop1);
    var stop2 = document.createElementNS("http://www.w3.org/2000/svg", "stop");
    stop2.setAttribute("stop-color", "rgba(0,0,0,0.5)");
    stop2.setAttribute("offset", "1000%");
    linearGradientBackground.appendChild(stop2);
    defs.appendChild(linearGradient);
    defs.appendChild(linearGradientBackground);
    this.svg.appendChild(defs);
    this.defs = defs;
  }

  createSlice() {
    let slice = document.createElementNS("http://www.w3.org/2000/svg", "path");
    slice.setAttribute("fill", "none");
    slice.setAttribute("stroke", "url(#gradient)");
    slice.setAttribute("stroke-width", this.strokeWidth);
    slice.setAttribute(
      "transform",
      `translate(${this.strokeWidth / 2},${this.strokeWidth / 2})`
    );
    slice.setAttribute("class", "animate-draw");
    this.svg.appendChild(slice);
    this.slice = slice;
  }

  createOverlay() {
    const r = this.size - this.size / 2 - this.strokeWidth / 2;
    const circle = document.createElementNS(
      "http://www.w3.org/2000/svg",
      "circle"
    );
    circle.setAttribute("cx", this.size / 2);
    circle.setAttribute("cy", this.size / 2);
    circle.setAttribute("r", r);
    circle.setAttribute("fill", "url(#gradient-background)");
    circle.setAttribute("class", "animate-draw");
    this.svg.appendChild(circle);
    this.overlay = circle;
  }

  createText() {
    const fontSize = this.size / 3.5;
    let text = document.createElementNS("http://www.w3.org/2000/svg", "text");
    text.setAttribute("x", this.size / 2 + fontSize / 7.5);
    text.setAttribute("y", this.size / 2 + fontSize / 4);
    text.setAttribute("font-family", "Century Gothic Lato");
    text.setAttribute("font-size", fontSize);
    text.setAttribute("fill", "#78f8ec");
    text.setAttribute("text-anchor", "middle");
    const tspanSize = fontSize / 3;
    text.innerHTML = `${0}% `;
    this.svg.appendChild(text);
    this.text = text;
  }

  createArrow() {
    var arrowSize = this.size / 10;
    var mapDir = {
      up: [(arrowYOffset = arrowSize / 2), (m = -1)],
      down: [(arrowYOffset = 0), (m = 1)]
    };
    function getDirection(i) {
      return mapDir[i];
    }
    var [arrowYOffset, m] = getDirection(this.direction);

    let arrowPosX = this.size / 2 - arrowSize / 2,
      arrowPosY = this.size - this.size / 3 + arrowYOffset,
      arrowDOffset = m * (arrowSize / 1.5),
      arrow = document.createElementNS("http://www.w3.org/2000/svg", "path");
    arrow.setAttribute(
      "d",
      `M 0 0 ${arrowSize} 0 ${arrowSize / 2} ${arrowDOffset} 0 0 Z`
    );
    arrow.setAttribute("fill", "none");
    arrow.setAttribute("opacity", "0.6");
    arrow.setAttribute("transform", `translate(${arrowPosX},${arrowPosY})`);
    this.svg.appendChild(arrow);
    this.arrow = arrow;
  }

  animateStart() {
    let v = 0;
    const intervalOne = setInterval(() => {
      const p = +(v / this.value).toFixed(2);
      const a = p < 0.95 ? 2 - 2 * p : 0.05;
      v += a;
      if (v >= +this.value) {
        v = this.value;
        clearInterval(intervalOne);
      }
      this.setValue(v);
    }, 10);
  }

  polarToCartesian(centerX, centerY, radius, angleInDegrees) {
    const angleInRadians = ((angleInDegrees - 180) * Math.PI) / 180.0;
    return {
      x: centerX + radius * Math.cos(angleInRadians),
      y: centerY + radius * Math.sin(angleInRadians)
    };
  }

  describeArc(x, y, radius, startAngle, endAngle) {
    const start = this.polarToCartesian(x, y, radius, endAngle);
    const end = this.polarToCartesian(x, y, radius, startAngle);
    const largeArcFlag = endAngle - startAngle <= 180 ? "0" : "1";
    const d = [
      "M",
      start.x,
      start.y,
      "A",
      radius,
      radius,
      0,
      largeArcFlag,
      0,
      end.x,
      end.y
    ].join(" ");
    return d;
  }

  setValue(value) {
    let c = (value / 100) * 360;
    if (c === 360) c = 359.99;
    const xy = this.size / 2 - this.strokeWidth / 2;
    const d = this.describeArc(xy, xy, xy, 180, 180 + c);
    this.slice.setAttribute("d", d);
    const tspanSize = this.size / 3.5 / 3;
    this.text.innerHTML = `${Math.floor(value)}% `;
  }

  animateReset() {
    this.setValue(0);
  }
}

const containers = document.getElementsByClassName("chart");
const dial = new Dial(containers[0]);
dial.animateStart();
				
			

The round dial inside our progress bar is made and animated using the Dial class. A class constructor that pulls attributes and data values from the HTML container—like size, stroke width, value, and arrow orientation—has been developed.

For our circular progress bar, we have created an SVG element using the createSVGElement function. This component will be used to decorate our progress bar with overlays, gradients, text, and arrow indicators.

In order to use this Dial component, the code first creates an instance of Dial, then selects HTML elements that have the class “chart” and initiates the animation.

Output

Code by: mohsen alizadeh

Project Link – Codepen

Written by: cssiseasy

CodePen H

Customization

Once a simple circular progress bar has been created, it may be adapted to your project and made to suit your requirements. Here are a few instances:

Change the colors: Adjust the progress bar’s background and the container’s border to complement the color scheme of your website.
Make a size adjustment: Play around with the dimensions of the progress container and bar.
Add text: To show the current displayed percentage, add text to your progress bar or surrounding it.
Animation: When the progress updates, use different CSS styles to create unique animations. 

Conclusion

I hope you now understand how I used HTML, CSS, and JavaScript to construct this circular progress bar.

If you find this blog useful, be sure to follow the cssiseasy.com Instagram page for future updates and do a Google search for “cssiseasy front-end projects with source code” for front-end projects with source code.

Visit Our More Articles – 

Facebook
Twitter
LinkedIn
Telegram
Email
WhatsApp

1 thought on “Creating a Curved Progress Bar Using JavaScript”

  1. Pingback: Email Validation Using Javascript: A Step-by-Step Tutorial

Leave a Comment

Your email address will not be published. Required fields are marked *

Scroll to Top