Last Updated:

Color Progress bar (Circle) in percentage using Javascript

The progress bar is used on sites when you need to show that the process of downloading information is underway or display the amount of data entered by the user, for example, when registering, placing an order, filling out a questionnaire, passing a test, etc.

In this article, I will tell you how to create an animated color progress bar in the form of a circle using the HTML5 element <canvas>.


Color Progress bar


Let's draw up a technical task for creating a progress bar in the form of a circle:

  1. The progress bar should be able to be configured for a specific task, in the settings you can change:
    - the start and end angles of the progress bar;
    is the radius;
    – thickness;
    – the palette and the number of colors used;
    is the speed of the animation.
  2. Ability to manage individual sectors of the progress bar using event handlers.
  3. Filling the progress of the bar should be gradient using multiple colors.
  4. The progress of the bar should be displayed.

At the stage of studying the work of the animated progress bar, we will use the "Start" button to launch it.

HTML-layout of the progress bar.

As already mentioned, the progress bar will be created on the basis of the canvas element. This element is designed to create graphics using JavaSript. Accordingly, the HTML layout is very simple and contains only two main elements: <canvas> and a button that starts drawing the progress bar.

Style sheet to create a progress bar.

A small style sheet that defines the appearance of the progress bar and button.

Write JavaScript to create a progress bar.

To limit the scope of our script and avoid conflicts with other js scripts, let's place the code in an anonymous self-starting function.

First of all, it is necessary to establish the environment (get the object of the element and its context), as well as to initialize a number of global, within the scope of the anonymous function, objects and variables that we will need in the future. After that, start the rendering setting of the color progress bar.<canvas>

Customize the color progress bar.

To evenly distribute the gradient around the circle, break the progress bar into seven parts (sectors). The size of one sector is 45 degrees. This separation, in addition to a uniform gradient, will allow you to connect seven event handlers. Each handler will start rendering the next sector. In this case, this sector will begin with the color in which the end of the previous one was painted. Due to this, the smoothness of the color transition between sectors is ensured.

We will repeatedly have to translate degrees into radians. To do this, let's write a small function:getRadians

Now the code of the function itself with detailed comments:init

As can be seen from the above JS-code, when you click on the "start" button, the progress bar drawing starts.
Before we start writing the code for this function, let's look at the detailed drawing algorithm.

Algorithm for drawing a color progress bar.

  • 1

    We get from the array containing the color palette of the progress bar, a pair that will be used as the beginning and end of the gradient of the current sector.options.colors

  • 2

    We get the coordinates of X, Y points of the beginning and end of the current sector of the progress bar. This can be done using the following formulas:
    x = xc + r * cos(a)
    y = yc + r * sin(a)

    where xc and yc are the coordinates of the center of the circle, r is the radius, a is the angle.

    I want to remind you that the upper left corner of the <canvas> element is taken as the start of the countdown.


    Color Progress bar
  • 3

    Create an object , which we will use to stroke the circle sector:gradient

    1. Using the , method, create a linear gradient object . As arguments, the method takes the values of the coordinates of the beginning and end of the sector to which it will be applied.createLinearGradientgradient
    2. Using the method, we define the color at the beginning and end of the object.addColorStopgradient
    3. Using the method, assign the resulting gradient to stroke the sector.strokeStyle
  • 4

    Fix the start time of the animation of drawing the current sector and start the animation.

The following is the JS code that implements this algorithm:

Remember, this is very important.

When creating an animation in JavaScript, use the . This feature allows you to synchronize animations with the browser's built-in page refresh mechanisms. The result will be a more efficient use of the graphics accelerator, re-processing of the same sections of the page will be excluded, there will be less CPU usage and, most importantly, the animation will be smoother, without jerks and twitches. setIntervalrequestAnimationFrame

Animation drawing of one sector of the color progress bar.

Let's now look at the principle of linear program animation as applied to our problem. To do this, it is necessary to determine a number of parameters with which we will carry out calculations:

Animation time in ms, set in the settings;
Sector size in rad, set in settings;
The time elapsed since the beginning of the animation is the difference between the current time and the start time of the animation.start

Based on the parameters obtained, it is possible to calculate at what angle the sector will be drawn (painted) in the current iteration:

Now you can go directly to the coloring of the sector. To do this, use the function , which will paint part of the sector at an angle equal to . When the function is called, the following parameters are passed to it:
— the color used to render part of the current sector;
– thickness of the progress bar;
is the angle at which the current sector will be drawn.drawSectorinc

Before you start writing the JS-code of the function, you need to understand how the phased coloring of the sector occurs:fn


As you can see from the figure, with each new iteration, the sector is painted each time from the beginning, but at a greater angle, while the layers with painted areas at each iteration are superimposed on each other. Due to the repeated overlapping of layers, the edges of the bar's progress are torn. To avoid this, you need to paint the gradient drawn in the previous iteration with a background color, and then apply a gradient. To do this, call the function twice: on the first call, the first parameter will be the background color, and on the second call , .drawSectorgradient

Compare the time elapsed since the beginning of the animation () with the time allotted to the animation ().
If , then continue to paint the current sector by recursively calling the function using the .
Otherwise, we will assume that the current sector is completely painted over with a gradient and proceed to the drawing of the next sector. To do this, increase the index by one to select the next pair from the array of colors, and increase the angle from which the next sector begins to be drawn by and call the function again .nowoption.durationnow < option.durationfnrequestAnimationFrameioptions.stepdraw

Let's imagine this as JS code and add this code to the end of the function:fn

What if the current sector was the last? In this case, you must abort the .
To solve this problem, the index . You can compare its value with the number of sectors that need to be drawn or with a long array containing a set of colors to paint the progress bar. I decided to settle on the second option.drawi

Let's add the following JS code to the function:fn

Now the full code of the function looks like this:draw

It remains to consider the JS code of the . The code is very easy to understand and comments inside the function itself will suffice:drawSector

Substrate under the colored progress bar in the form of a circle.


So, the progress bar is drawn taking into account all the settings we set. But... Look at what part of the page looks like before the progress bar is rendered - a fairly large empty space above the start button.

Let's draw a substrate on top of which it will be located on the site of the future progress bar.

Color Progress bar


To draw a watermark, we will use the same function as for drawing a sector - , calling it from the function . We will not use animation when creating a watermark.
Updated JS-code of the function with addition:drawSectorinitinit

Let's also add an addition to the function - calculating the angle of completion of rendering depending on the parameter .

Updated JS-code of the function with addition:drawSectorincdrawSector

Output of filling color bar progress in percentage

To improve the visualization, let's add a display of the fill bar progress in percentage. To do this, create a function , which will be called from the function responsible for the rendering animation of the progress bar.
The parameters of the function will be: the number of the current sector and the angle of rendering of the current sector.showPercentsfniinc

In the function , after the lines:fn

let's add a function call :showPercents

The JS-code of the function itself is simple, the comments given in it will be enough to understand it:showPercents

So, let's look at the result of our script:


Color Progress bar


As you can see in the screenshot, the gradient is not solid, the joints of the sectors that are not shaded to the end are visible. Most likely, this bug occurs when converting degrees to radians due to rounding of the result of calculations. Let's try to fix this bug.

The boundaries of the sectors of the colored progress bar in the form of a circle.


To eliminate the identified drawback, it is necessary to additionally paint over the joints of the sectors by drawing a line over the sector boundary. There are two options for drawing sector boundaries:

  1. paint over the joints of the sectors with a color that contrasts with the color palette of the progress bar;
  2. use the color of the current gradient to paint the junction of sectors.
Color Progress bar

The JS code for both options is almost identical. The only difference is that in the first case, we choose the color of the border ourselves. In the second case, the color is obtained from the array depending on the value of the variable , which is the number of the currently painted sector.options.colorsi

The JS code that draws the sector boundary is implemented in a function that takes one parameter- the current sector number.drawLine

As you can see from the code, all the difference consists of the value set to the property , responsible for the color of the drawn line.ctx.strokeStyle

A function from a function is called drawLinefn

Use a colored progress bar for multiple events.


The JS code we reviewed draws a color progress bar completely after clicking on the "Srart" button. And what if we want the progress bar to display the stages of passing the test, the completeness of filling out the questionnaire, etc.? There is nothing complicated in this, it is enough to make a couple of changes and additions to the existing code:

  1. From the function, you must remove the recursive call to this function, which starts drawing the following sector: draw
  2. At the end of the function, add additional event handler logs that will call the function to draw the next sector of the color progress bar. where button1, button2, ..., buttonN are the DOM elements that need to be clicked on to render the next sector of the progress bar. initdraw


Readers who are familiar with canvas may wonder why use sectors if you need a progress bar for a single event. Using the method, you can set the color of the gradient at various points of the progress bar, for example:addColorStop

Unfortunately, for the progress of the bar in the form of a circle, this method is not suitable, because the gradient spreads in a straight line, from left to right, and not along the length of the circle. The result will be far from expected. Therefore, it is better to divide the circle into sectors. At the same time, the greater the number of sectors (within reason), the more uniform the gradient.