Create Animated Background Changer With Explosion
By Prgmaz 04 Jan 2021

alt text

What we need for this tutorial:

  • HTML and CSS
  • JQuery and Bootstrap
  • AnimeJS
  • and your choice of editor

I downloaded all the assets and placed them in my assets directory so my directory will look like this:

  • animations/assets/css/css-files-here
  • animations/assets/js/js-files-here

Then I created index.html file and styles.css in my assets folder and then linked all css in header tag and linked all js files below the body.

So my index.html is looking like this:

<!doctype html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport"
          content="width=device-width, user-scalable=no, initial-scale=1.0, maximum-scale=1.0, minimum-scale=1.0">
    <meta http-equiv="X-UA-Compatible" content="ie=edge">
    <title>Home - Programmer101N</title>
    <link rel="stylesheet" href="assets/css/bootstrap.min.css">
    <link rel="stylesheet" href="assets/css/styles.css">
</head>
<body>

</body>
<script src="assets/js/jquery.min.js"></script>
<script src="assets/js/bootstrap.min.js"></script>
<script src="assets/js/anime.js"></script>
</html>

Let’s Get Started on our effect.

First let’s create a canvas element in our body and give it id of canvas, This will contain our animations.

<body>
<canvas id="#canvas"></canvas>
</body>

That’s all you need to do in HTML part. Let’s Jump into CSS Part.

So you can copy css from below.

html, body{
    height: 100vh;
    width: 100vw;
    display: flex;
    margin: 0;
    padding: 0;
}

#canvas{
    background-color: transparent;
}

What I’m doing here in css is that set body width and height to full and remove any margin and padding and set display to flexbox.

Let’s jump into JS part now. Open script tag under body tag.

</body>
...
<script>

</script>

Let’s Create some variables first. Variables we are gonna need our mentioned below:

  • Canvas element
  • Canvas Context
  • Number of Particles
  • Colors and Background Colors
  • and a counter
const canvas = document.getElementById("canvas");
const c = canvas.getContext('2d');

const nParticles = 20;
let colors = ['#FF1461', '#18FF92', '#5A87FF', '#FBF38C'];
let circleColors = ['#91133c', '#27a969', '#224bbf', '#888220'];
let i = 0;

First I’m going to set height and width of canvas to innerHeight and innerWidth of window.

const canvas = document.getElementById("canvas");
const c = canvas.getContext('2d');
canvas.width = window.innerWidth;
canvas.height = window.innerHeight;

const nParticles = 20;
let colors = ['#FF1461', '#18FF92', '#5A87FF', '#FBF38C'];
let circleColors = ['#91133c', '#27a969', '#224bbf', '#888220'];
let i = 0;

Now I’m going to add listener to resize event, This event is called when We resize our window. I’m going to resize our canvas when this is called.

window.addEventListener('resize', () => {
    canvas.width = window.innerWidth;
    canvas.height = window.innerHeight;
});

Now let’s create our particles. We need to define two functions, getParticle and second getEndPos.

getParticle function create particles with arguments given to the function like positioning, color, and radius.

function getParticle(x, y){
    const p = {
        x: x,
        y: y,
        radius: anime.random(18, 32),
        color: colors[anime.random(0, colors.length-1)],
        draw: () => {
            c.beginPath();
            c.arc(p.x, p.y, p.radius, 0, Math.PI * 2, true);
            c.fillStyle = p.color;
            c.fill();
        }
    }

    p.endPos = getEndPos(p);
    return p;
}

getEndPos function calculate random Position around start location where the particle should go or animated to.

function getEndPos(p) {
    let angle = anime.random(0, 360) * Math.PI / 180;
    let radius = [-1, 1][anime.random(0, 1)] * anime.random(50, 180);
    return {
        x: p.x + radius * Math.cos(angle),
        y: p.y + radius * Math.sin(angle),
    }
}

Now let’s create our Explosion effect.

so we’re gonna create another function Explosion that’s going to take x and y position of clicks and pass it to our getParticle function in loop.

function explosion(x, y){
    const animatable = [];

    for(let i = 0 ; i < nParticles; i++){
        animatable.push(getParticle(x, y));
    }
}

Now let’s create a circle to change Background.

We are gonna create a circle that occupies whole canvas area with incremental background color from our Variable circleColors.

function explosion(x, y){
    ...
    const circle = {
        x: x,
        y: y,
        radius: 0,
        color: circleColors[i++],
        draw: () => {
            c.beginPath();
            c.arc(circle.x, circle.y, circle.radius, 0, Math.PI * 2, true);
            c.fillStyle = circle.color;
            c.fill();
        }
    }

    if (i >= circleColors.length){
        i = 0;
    }
}

Now let’s animate our Circle.

function explosion(x, y){
    ...
    anime({
        targets: circle,
        radius: window.innerWidth * 1.125,
        complete: () => {
            document.body.style.background = circle.color;
        },
        update: (e) => {
            e.animatables.forEach((a) => {
                a.target.draw();
            });
        },
        duration: 3000,
        easing: 'easeOutExpo',
    });
}

Let’s Animated Particles.

function explosion(x, y){
    ...
    anime({
        targets: animatable,
        radius: 0.1,
        x: (p) => p.endPos.x,
        y: (p) => p.endPos.y,
        update: (e) => {
            e.animatables.forEach((a) => {
                a.target.draw();
            });
        },
        duration: 2000,
        easing: 'easeOutExpo'
    });
}

So now your explosion function should look like this.

function explosion(x, y){
    const animatable = [];

    for(let i = 0 ; i < nParticles; i++){
        animatable.push(getParticle(x, y));
    }

    const circle = {
        x: x,
        y: y,
        radius: 0,
        color: circleColors[i++],
        draw: () => {
            c.beginPath();
            c.arc(circle.x, circle.y, circle.radius, 0, Math.PI * 2, true);
            c.fillStyle = circle.color;
            c.fill();
        }
    }

    if (i >= circleColors.length){
        i = 0;
    }

    anime({
        targets: circle,
        radius: window.innerWidth * 1.125,
        complete: () => {
            document.body.style.background = circle.color;
        },
        update: (e) => {
            e.animatables.forEach((a) => {
                a.target.draw();
            });
        },
        duration: 3000,
        easing: 'easeOutExpo',
    });

    anime({
        targets: animatable,
        radius: 0.1,
        x: (p) => p.endPos.x,
        y: (p) => p.endPos.y,
        update: (e) => {
            e.animatables.forEach((a) => {
                a.target.draw();
            });
        },
        duration: 2000,
        easing: 'easeOutExpo'
    });

}

Now we will create a cycle that will clear our canvas.

const render = anime({
    duration: Infinity,
    update: () => {
        c.clearRect(0, 0, canvas.width, canvas.height);
    }
});

Now Let’s add a event listener to detect touches or taps in our window and call explosion function on where the touch started.

document.addEventListener(('ontouchstart' in window || navigator.msMaxTouchPoints) ? 'touchstart' : 'mousedown', (e) => {
    render.play();
    explosion(e.clientX, e.clientY);
});

If you did everything correctly your script should look like this.

<script>
    const canvas = document.getElementById("canvas");
    const c = canvas.getContext('2d');
    canvas.width = window.innerWidth;
    canvas.height = window.innerHeight;

    const nParticles = 20;
    let colors = ['#FF1461', '#18FF92', '#5A87FF', '#FBF38C'];
    let circleColors = ['#91133c', '#27a969', '#224bbf', '#888220'];
    let i = 0;

    window.addEventListener('resize', () => {
        canvas.width = window.innerWidth;
        canvas.height = window.innerHeight;
    });

    function getEndPos(p) {
        let angle = anime.random(0, 360) * Math.PI / 180;
        let radius = [-1, 1][anime.random(0, 1)] * anime.random(50, 180);
        return {
            x: p.x + radius * Math.cos(angle),
            y: p.y + radius * Math.sin(angle),
        }
    }

    function getParticle(x, y){
        const p = {
            x: x,
            y: y,
            radius: anime.random(18, 32),
            color: colors[anime.random(0, colors.length-1)],
            draw: () => {
                c.beginPath();
                c.arc(p.x, p.y, p.radius, 0, Math.PI * 2, true);
                c.fillStyle = p.color;
                c.fill();
            }
        }

        p.endPos = getEndPos(p);
        return p;
    }

    function explosion(x, y){
        const animatable = [];

        for(let i = 0 ; i < nParticles; i++){
            animatable.push(getParticle(x, y));
        }

        const circle = {
            x: x,
            y: y,
            radius: 0,
            color: circleColors[i++],
            draw: () => {
                c.beginPath();
                c.arc(circle.x, circle.y, circle.radius, 0, Math.PI * 2, true);
                c.fillStyle = circle.color;
                c.fill();
            }
        }

        if (i >= circleColors.length){
            i = 0;
        }

        anime({
            targets: circle,
            radius: window.innerWidth * 1.125,
            complete: () => {
                document.body.style.background = circle.color;
            },
            update: (e) => {
                e.animatables.forEach((a) => {
                    a.target.draw();
                });
            },
            duration: 3000,
            easing: 'easeOutExpo',
        });

        anime({
            targets: animatable,
            radius: 0.1,
            x: (p) => p.endPos.x,
            y: (p) => p.endPos.y,
            update: (e) => {
                e.animatables.forEach((a) => {
                    a.target.draw();
                });
            },
            duration: 2000,
            easing: 'easeOutExpo'
        });

    }

    const render = anime({
        duration: Infinity,
        update: () => {
            c.clearRect(0, 0, canvas.width, canvas.height);
        }
    });

    document.addEventListener(('ontouchstart' in window || navigator.msMaxTouchPoints) ? 'touchstart' : 'mousedown', (e) => {
        render.play();
        explosion(e.clientX, e.clientY);
    });

</script>

Our animation is now finished. You can implement it on your own website or Try to create it just for fun.

Effect Image

You can download this source code from my Github.

Visit this Project on Github

Naman Baranwal
Hi, I’m Prgmaz. I’m a Web, Game and App Developer.

Tags