Create an Animated Favicon Loader with JavaScript

Favicons are a essential partial of online branding, they give a visible cue to users, and assistance them distinguish your site from others. Although many favicons are static, it is probable to create charcterised favicons as well.

Let’s Talk About a Importance of Favicons

Let’s Talk About a Importance of Favicons

Online business veterans know it’s a small things that set a association detached from a competition. Newbies will…Read more

A constantly relocating favicon is positively irritating for many users, and also harms accessibility, however when it’s usually charcterised for a brief time in response to a user movement or a credentials event, such as a page load, it can provide additional visible information—therefore improving user experience.

In this post, I’ll uncover we how to emanate an animated round loader in a HTML canvas, and how we can use it as a favicon. An animated favicon loader is a good apparatus to visualize a swell of any action achieved on a page, such as record uploading or picture processing. You can have a demeanour during a demo belonging to this tutorial on Github as well.

1. Create a canvas element

First, we need to create a board animation that draws a full circle, 100 percent in total (this will be critical when we need to increment a arc).

button id=lbtnLoad/button
canvas id=cvl width=16 height=16/canvas

I’m regulating a customary favicon size, 16*16 pixels, for a canvas. You can use a distance bigger than that if we want, though note that a board picture will be scaled down to a 162 pixel area when it’s practical as a favicon.

2. Check if canvas is supported

Inside a onload() eventuality handler, we get a anxiety for a board element [cv] regulating a querySelector() method, and impute its 2D sketch context object [ctx] with a assistance of a getContext() method.

onload = function() {
    cv = document.querySelector('#cvl'),
    ctx = cv.getContext('2d');

    if (!!ctx) {
        /* ... */
    }
};

We also need to check if a board is upheld by a UA by creation certain that a sketch context intent [ctx] exists and isn’t undefined. We’ll place all a regulation belonging to a bucket event into this if condition.

3. Create a initial variables

Let’s emanate three some-more tellurian variables, s for a starting angle of a arc, tc for a id for a setInterval() timer, and pct for a percentage value of a same timer. The regulation tc = pct = 0 assigns 0 as a initial value for a tc and pct variables.

onload = function() {
    cv = document.querySelector('#cvl'),
    ctx = cv.getContext('2d');

    if (!!ctx) {
        s = 1.5 * Math.PI,
        tc = pct = 0;
    }
};

To uncover how a value of s was calculated, let me fast explain how arc angles work.

Arc angles

The subtended angle (the angle stoical of a dual rays that conclude an arc) of a rim of a circle is 2π rad, where rad is a radian territory symbol. This creates a angle for a entertain arc equal to 0.5π rad.

When visualizing a loading progress, we wish a round on a board to be drawn from a tip position rather than a default right.

Going clockwise (default instruction arc is drawn on a canvas) from a right position, a tip indicate is reached after 3 quarters, i.e. during an angle of 1.5π rad. Hence, I’ve combined a non-static s = 1.5 * Math.PI to after denote a starting angle for a arcs to be drawn from on a canvas.

4. Style a circle

For a sketch context object, we conclude a lineWidth and strokeStyle properties of a circle we are going to pull in a subsequent step. The strokeStyle skill stands for a color.

onload = function() {
    cv = document.querySelector('#cvl'),
    ctx = cv.getContext('2d');

    if (!!ctx) {
        s = 1.5 * Math.PI,
        tc = pct = 0;

        ctx.lineWidth = 2;
        ctx.strokeStyle = 'fuchsia';
    }
};

5. Draw a circle

We add a click eventuality handler to a Load symbol [#lbtn] that triggers a setInterval timer of 60 milliseconds, that executes a duty obliged for sketch a round [updateLoader()] any 60ms compartment a round is entirely drawn.

The setInterval() process returns a timer id to brand a timer that is reserved to a tc variable.

onload = function() {
    cv = document.querySelector('#cvl'),
    ctx = cv.getContext('2d');

    if (!!ctx) {
        s = 1.5 * Math.PI,
        tc = pct = 0,
        btn = document.querySelector('#lbtn');

        ctx.lineWidth = 2;
        ctx.strokeStyle = 'fuchsia';

        btn.addEventListener('click', function() {
            tc = setInterval(updateLoader, 60);
        });
    }
};

6. Create a updateLoader() tradition function

It’s time to emanate a tradition updateLoader() duty that is to be called by a setInterval() method when a symbol is clicked (the eventuality is triggered). Let me uncover we a regulation first, afterwards we can go along with a explanation.

function updateLoader() {
    ctx.clearRect(0, 0, 16, 16);
    ctx.beginPath();
    ctx.arc(8, 8, 6, s, (pct * 2 * Math.PI / 100 + s));
    ctx.stroke();

    if (pct === 100) {
        clearInterval(tc);
        return;
    }

    pct++;
}

The clearRect() process clears a rectilinear area of a canvas tangible by a parameters: a (x, y) coordinates of a top-left corner. The clearRect(0, 0, 16, 16) line erases everything in a 16*16 pixels board we have created.

The beginPath() process creates a new path for a drawing, and a stroke() process paints on that newly combined path.

At a finish of a updateLoader() function, a percentage count [pct] is incremented by 1, and before to a increment we check if it equals to 100. When it’s 100 percent, a setInterval() timer (identified by a timer id, tc) is cleared with a assistance of a clearInterval() method.

The initial 3 parameters of a arc() process are a (x, y) coordinates of core of a arc and its radius. The fourth and fifth parameters paint a start and finish angles during that a sketch of a arc starts and ends.

We already motionless a starting indicate of a loader circle, that is during a angle s, and it’ll be a same in all a iterations.

The finish angle however will increment with a percent count, we can calculate a size of a increment in a following way. Say 1% (the value 1 out of 100) is equivalent to angle α out of 2π in a circle (2π = angle of a whole circumference), afterwards a same can be created as a following equation:

1/100 = α/2π

On rearranging a equation:

α = 1 * 2π /100
α = 2π/100

So, 1% is homogeneous to a angle 2π/100 in a circle. Thus, a finish angle during any percent increment is computed by augmenting 2π/100 by a commission value. Then a outcome is added to s (start angle), so a arcs are drawn from a same starting position any time. This is because we used a pct * 2 * Math.PI / 100 + s regulation to calculate a finish angle in a regulation dash above.

7. Add a favicon

Let’s place a favicon couple element into a HTML head section, possibly directly or around JavaScript.

link rel="icon" type="image/ico" 

In a updateLoader() function, initial we fetch a favicon regulating a querySelector() method, and allot it to a lnk variable. Then we need to export a board image any time an arc is drawn into an encoded image by regulating a toDataURL() method, and assign that information URI calm as a favicon image. This creates an charcterised favicon that is a same as a board loader.

onload = function() {
    cv = document.querySelector('#cvl'),
    ctx = cv.getContext('2d');

    if (!!ctx) {
        s = 1.5 * Math.PI,
        tc = pct = 0,
        btn = document.querySelector('#lbtn'),
        lnk = document.querySelector('link[rel="icon"]');

        ctx.lineWidth = 2;
        ctx.strokeStyle = 'fuchsia';

        btn.addEventListener('click', function() {
            tc = setInterval(updateLoader, 60);
        });
    }
};

function updateLoader() {
    ctx.clearRect(0, 0, 16, 16);
    ctx.beginPath();
    ctx.arc(8, 8, 6, s, (pct * 2 * Math.PI / 100 + s));
    ctx.stroke();

    lnk.href= cv.toDataURL('image/png');

    if (pct === 100) {
        clearTimeout(tc);
        return;
    }

    pct++;
}

You can have a demeanour during a full regulation on Github.

Bonus: Use a loader for async events

When we need to use this board animation in and with a loading action in a web page, allot a updateLoader() duty as a eventuality handler for a progress() eventuality of a action.

For instance, a JavaScript will change like this in AJAX:

onload = function() {
    cv = document.querySelector('#cvl'),
    ctx = cv.getContext('2d');

    if (!!ctx) {
        s = 1.5 * Math.PI,
        lnk = document.querySelector('link[rel="icon"]');

        ctx.lineWidth = 2;
        ctx.strokeStyle = 'fuchsia';
    }

    var xhr = new XMLHttpRequest();
    xhr.addEventListener('progress', updateLoader);
    xhr.open('GET', 'https://xyz.com/abc');
    xhr.send();
};

function updateLoader(evt) {
    ctx.clearRect(0, 0, 16, 16);
    ctx.beginPath();
    ctx.arc(8, 8, 6, s, (evt.loaded*2*Math.PI/evt.total+s));
    ctx.stroke();

    lnk.href = cv.toDataURL('image/png');
}

In a arc() method, reinstate a commission value [pct] with a loaded skill of a event—it denotes how most of a record has been loaded, and in place of 100 use a total skill of a ProgressEvent, that denotes a sum volume to be loaded.

There’s no need for setInterval() in such cases, as a progress() eventuality is automatically fired as a loading progresses.

Add Comment