Spinner — Loader

Recently I've been doing a lot of with HTML5 canvas and Javascript; much of the time including an Ajax request that may take some time to request and process the returned data. Needless to say the requirement to provide some meaningful feedback that ‘things are happening’ often needs to be implemented. With that in mind here’s my take on an HTML5 Canvas loading spinner.

I’d like a number of spokes in a circle around a centre point. This can easily be done using rotate transforms. Draw a line, rotate the canvas, draw another line, rinse and repeat until enough spokes have been drawn to create a circle. If we want 12 spokes, we need to rotate a 12th of a circle each time. Remember from my ellipse post that the canvas API uses radians and not degrees, so for a full circle we use 2π radians which is equal to 360° degrees. A 12th of a circle would therefore be 2π/12

for (i = 0; i < 12; i++) {
ctx.rotate(Math.PI * 2 / 12); // Rotate a 12th of 360 degrees
ctx.moveTo(4, 0);
ctx.lineTo(8, 0);

This gives us the base for our loading spinner.

To help with our spinning effect we'll fade each spoke as we progress around the circle. This would be an ideal candidate a lerp‡ function as we may want to place our spinner on a differently coloured background and not have alpha (transparency) channels. In this case though, we'll reduce the alpha value by a 12th before we make each stroke.

ctx.strokeStyle = "rgba(0,0,0," + i / 12 + ")";

Note: Lerp is an abbreviation for linear interpolation, which is a function used to derive an unknown value that falls between two known values i.e. 25% between black and white.

Now we need to animate the spinner, which we'll do with our favourite recurring loop, as discussed in my javascript universe post. In the loop we'll clear the canvas rotate and redraw our spinner. To clear the canvas we can just use the clearRect() method.

ctx.clearRect(0, 0, ctx.canvas.width, ctx.canvas.height);

To get the spinner to make one full rotation per second we need to know how long it’s have been spinning for, and to calculate the elapsed amount of seconds (which will be the amount of times we will have rotated).

var rotationsSinceStarted = (now - started) / 1000;
context.rotate(Math.PI * 2 * rotationsSinceStarted);
NB: animated gif

Note: Since we know that were doing one full rotation per second, we could have further optimised by ignoring all but the fractions of a rotation\second. This can easily be done by using the modulo operator on the rotationsSinceStarted variable, ensuring that all whole number of rotations are discarded. In this case though this type of operation isn't needed.

However, this isn't exactly what we're after. It would be more visually appealing if each spoke lights up and fades as time progresses. To accomplish this we'll need to ensure that we only rotate in whole 12ths. (12 being the quantity of spokes we have.)

var rotation = parseInt(rotationsSinceStarted * 12) / 12;
ctx.rotate(Math.PI * 2 * rotation);
NB: animated gif

And there’s our loading spinner, and with a few modifications we can increase or decrease the speed, modify the colour and\or the quantity of spokes. We could even change the shape of the spokes.