Creating a loading progress bar animation with HTML5 Canvas

In this HTML5 canvas example I create a simple loading progress bar demonstrating how you can use animation in a canvas.

Creating an animation function

Like all animation, the HTML5 canvas can create moving images by rapidly changing the image you are viewing. This is done by having a function that will be called between 30 to 60 times a second. I will be using this to quickly draw a picture to make it look like its a continuously changing picture.

In most browsers there is a useful function which takes a callback called window.requestAnimationFrame. This function tells the browser that you want to call a specific function before the next repaint. This is typically capped at 60 frames a second but might be lower if your code is executing in the background.

The animation function I will be using to draw my progress bar is as follows

window.requestForAnimation = (function(callback) {
    return window.requestAnimationFrame || window.webkitRequestAnimationFrame || window.mozRequestAnimationFrame || window.oRequestAnimationFrame || window.msRequestAnimationFrame ||
        function(callback) {
            window.setTimeout(callback, 1000 / 60);
        };
})();

Since this might not be supported by all browsers we also use the above code to attempt to find this method or methods like it. If all fails and no browser-specific methods are available we fall back to a simple callback that runs after 1/60th of a second. This means that if the browser doesn’t support requestAnimationFrame then we just call the animation using the basic setTimeout function. Depending on the level of browser support you want to target you could probably fall back to calling requestAnimationFrame directly but it is entirely up to your implementation.

Creating my draw function for the Progress bar

Now I have my animation function I am going to create a draw function. This will take the percentage to draw as a parameter and draw that percentage on the canvas.

var canvas = document.getElementById("mainCanvas");
var context = canvas.getContext("2d");
context.fillStyle = "black";
context.font = "20px Arial";
context.lineWidth = 10;

First I have to set up the canvas as above. Here I get the context and set up the fillStyle and lineWidth (used for the arc), and the font of the progress text. This won’t change throughout the example so I don’t need to set this in the function.

function drawPercentage(percentage) {
    //Clear the screen
    context.clearRect(0, 0, canvas.width, canvas.height);

    //Draw some text
    context.fillText(percentage + "%", 40, 65);

    //Draw a circle
    context.beginPath();

    //arcs start middle right of the circle so we tell it to start at Math.PI *0.5 which is the middle bottom
    var arcPercent = 0.5 + (2 * ( percentage / 100));
    context.arc(60, 60, 50, 0.5 * Math.PI, arcPercent*Math.PI);
    context.stroke();
}

The first thing I do in my draw function is clear the canvas. This is because each time you draw it does not remove the old images on the canvas. Once cleared I draw the percentage on the screen with fillText, specifying the text and the x and y position on the screen.

Here I am hardcoding the location of the text but in a later tutorial, I will improve how I place the text.

To draw the circle I start a new path with beginPath. Once this has been done I can start to work out how far the arc needs to be drawn.

All arcs start from the middle right hand side of a circle, the east side, this represents 0 on the arc. Since I want to start from the bottom southern side I need to start my arc using 0.5 * Math.PI. This is using radians to specify the angle to start at.

I calculate the finish at using the formulae 0.5 + (2 * ( percentage / 100)). This calculates a value from 0.5 to 2.5 depending on the percentage which I then use this calculated value multiplied by Math.PI as the final angle.

Once I have created the arc I finish off the drawing by calling stroke to create the line.

Animating the progress bar

Now I have a function to draw the progress bar I can start animating it. The animation function will use two variables to keep track of how long the animation has been running and what the current percentage shown is.

var animationStart = Date.now();
var currentPercentage = -1;

The time that the animation started will be used in the function to work out what percentage the animation should be at when the animate function is called. This is important because the animation function will be called when the browser is able to perform an animation call. Most of the time this will be roughly the same period of time but it is open to the browser.

So instead of assuming each time it is called a similar amount of time has passed we keep track of when the animation started to work out what the canvas should show.

function animateNextFrame() {
    var thisPercentage = Math.floor(((Date.now() - animationStart) / 50));
    if(thisPercentage > 100) {
        thisPercentage = 100;
    }

    if(thisPercentage !== currentPercentage) {
        drawPercentage(thisPercentage);
        currentPercentage = thisPercentage;
    }

    //Only request more animation until we have reached 100%
    if(thisPercentage < 100) {
        requestForAnimation(animateNextFrame);
    }
}

The first thing that the animation function does is use the animation start time to work out what percentage should be shown. Here I use the current time minus the start time of the animation divided by 50 (20 percentage points increase every second).

The next check is important and sets the percentage to 100 if it is above 100. This handles that the event that the animation call was delayed and the new percentage is larger than 100.

Then if the current percentage is different from the currently animated percentage we draw the new percentage and update what we have currently drawn.

Now this has been drawn (or not drawn if it hasn’t reached a new percentage) we request a new animation frame if we haven’t drawn the last percentage (100). If we have drawn the 100th percentage image then we no longer need to call the animation frame. This means that as soon as the final frame has been animated we have no further overhead.

Now we have our animated progress bar.

Conclusions on animating a progress bar

One important piece of information to note is that you have to consider that the animation function may not be called at even time intervals. Since it depends on the browser having time to execute the callback it is likely to be different time periods each call. Therefore we must handle drawing the animation based on how long has passed from the start which may involve skipping frames.

The example including the source code is available on my website.

If you have any questions or wish to learn more leave a comment below and I will endeavour to answer any questions.

One Comment

Leave a Reply

This site uses Akismet to reduce spam. Learn how your comment data is processed.