Simple Particle Animation in JavaScript

I’ve been hitting a few walls trying to learn how to do some simple sprite animation but here is simple JavaScript animation that was pretty fun to work with to see how you can code with the canvas.

This animation randomly drops a circle onto the canvas and was originally created by Josh Marinacci.

http://projects.joshy.org/presentations/HTML/CanvasDeepDive/presentation.html

Also check out his blog, he has some interesting stuff on there.

http://joshondesign.com/

My html side is virtually empty. I just made a canvas object.

<html>
    <body>
        <canvas width="200" height="200" id="canvas"/>
    </body>
</html>

I’m going to quickly break down each section. If you’re new to JavaScript remember that code executes top to bottom.

First up, globals variables. Variable scope is a pretty easy concept to understand. A global variables is a variable that they can be accessed anywhere in the script. There are three globals variables in this code. Canavas is the reference to the canvas object on the HTML code. Particles is an array that we will be using to store the animation particles and tick is just a simple ticker variable.

    var canvas = document.getElementById('canvas');
    var particles = [];
    var tick = 0;

The all the function loop does is call the other functions in the script. It’s repeated using the setInterval method below the function. The method, setInterval() takes two parameters, the loop function and a integer 30. Thirty represents the how long the the setInterval() method should wait before calling loop again, also it’s defined in milliseconds. If you want to make the animation faster insert a lower number and likewise use a larger number if you want to speed it up.

function loop() {
    createParticles();
    updateParticles();
    resetParticles();
    drawParticles();
}
setInterval(loop,30);

The function createParticles() adds objects to the particles array. The first if checks the ticker has a remainder of 0 when modular division of 10 is used. The next, nested if statement checks makes sure the array only holds 100 objects at a time. Inside the nested if statement, the push command is used to add an particle to the array. A particle object is made up of a x/y coordinate, speed rate it will fall at, radius of the circle, and a random color. The randomColor() fucntion is the only code I made myself. I’ll show you that later, but in Josh’s example every circle was white.

function createParticles() {
    if(tick % 10 == 0) {
        if(particles.length < 100) {
            particles.push({
                x: Math.random()*canvas.width,
                y: 0,
                speed: 1+Math.random()*2,
                radius: 2+Math.random()*5,
                color: randomColor(),
            });
        }
    }
}

This function is pretty simple. Instead of having all white circles, I decided to add a bit of color to them. I made a simple switch statement that will return a random color between red, white, blue, green, gold. I’ve also added an extra return color of purple before the function ends. My function randomColor() should never return purple but in the unlikely case a number out side the range of 0-4 is picked, the circle will be purple.

function randomColor(){
    // Random number between 0-4
    switch(Math.floor(Math.random()*5)){
        case 0:
	    return('red');
	case 1:
	    return('white');
	case 2:
	    return('blue');
	case 3:
	    return('green');
	case 4:
	    return('gold');
    }
// If there is purple circle, then something when wrong.
    return 'purple';
}

The function updateParticles controls how the particles fall down the canvas. All it is a simple for loop that adds the speed rate onto the particle’s y coordinate. I’ve also added code to make the particles wiggle as the fall down. You can also make the fall at an angle if you wish by doing something part.x += 1 or part.x -=1.

function updateParticles() {
    for(var i in particles) {
        var part = particles[i];
        part.y += part.speed;
        part.x += (-1+(Math.random()*2))
    }
}

In Josh’s example he had a function called killParticles but that seemed a little deceiving so I renamed it to resetParticles. I also had to add a little more logic to it. The this function core is a for loop that iterates through all the particles stored in the array. In Josh’s example he just rest the particles y coordinate if it went outside the canvas limits. Since I’m also moving the particles on the x coordinate I’ve added another if statement to check if the particles leave the canvas limits.

function resetParticles() {
    for(var i in particles) {
        var part = particles[i];
        if(part.y > canvas.height) {
            part.y = 0;
	    part.color = randomColor();
	    part.speed = 1+Math.random()*2;
            part.radius = 2+Math.random()*5;
        }

	if(part.x > canvas.width || part.x < 0){
	    part.y = 0;
	    part.x = Math.random()*canvas.width;
	    part.color = randomColor();
	    part.speed = 1+Math.random()*2;
            part.radius = 2+Math.random()*5;
	}
    }
}

The last function is drawParticles and this function draws the particles onto the canvas.

function drawParticles() {
    var cv = canvas.getContext('2d');
    cv.fillStyle = "black";
    cv.fillRect(0,0,canvas.width,canvas.height);

    for(var i in particles) {
        var part = particles[i];
        cv.beginPath();
        cv.arc(part.x,part.y, part.radius, 0, Math.PI*2);
        cv.closePath();
        cv.fillStyle = part.color;
        cv.fill();
    }
}

Finally, here is my full source code. After the my code I’ve also included Josh’s example code that I used to learn how to fiddle with the canvas.

<script>
    var canvas = document.getElementById('canvas');
    var particles = [];
    var tick = 0;

    function loop() {
        createParticles();
        updateParticles();
        resetParticles();
        drawParticles();
    }
    setInterval(loop,30);

    function createParticles() {
        if(tick % 10 == 0) {
            if(particles.length < 100) {
                particles.push({
                    x: Math.random()*canvas.width,
                    y: 0,
                    speed: 1+Math.random()*2,
                    radius: 2+Math.random()*5,
                    color: randomColor(),
                });
            }
        }
    }

    function randomColor(){
	// Random number between 0-4
	switch(Math.floor(Math.random()*5)){
		case 0:
			return('red');
		case 1:
			return('white');
		case 2:
			return('blue');
		case 3:
			return('green');
		case 4:
			return('gold');
	}
	// If there is purple circle, then something when wrong.
	return 'purple';
    }

    function updateParticles() {
        for(var i in particles) {
            var part = particles[i];
            part.y += part.speed;
            part.x += (-1+(Math.random()*2))
        }
    }

    function resetParticles() {
        for(var i in particles) {
            var part = particles[i];

            if(part.y > canvas.height) {
                part.y = 0;
		part.color = randomColor();
		part.speed = 1+Math.random()*2;
                part.radius = 2+Math.random()*5;
            }

	    if(part.x > canvas.width || part.x < 0)){
	        part.x = 0;
		part.color = randomColor();
		part.speed = 1+Math.random()*2;
                part.radius = 2+Math.random()*5;
	    }
        }
    }

    function drawParticles() {
        var cv = canvas.getContext('2d');
        cv.fillStyle = "black";
        cv.fillRect(0,0,canvas.width,canvas.height);

	for(var i in particles) {
            var part = particles[i];
            cv.beginPath();
            cv.arc(part.x,part.y, part.radius, 0, Math.PI*2);
            cv.closePath();
            cv.fillStyle = part.color;
            cv.fill();
        }
    }
</script>

This is Josh’s example, you can also see alive demo at http://projects.joshy.org/presentations/HTML/CanvasDeepDive/snow_sim.html

<script>

var canvas = document.getElementById('canvas');
var particles = [];
var tick = 0;
function loop() {
    createParticles();
    updateParticles();
    killParticles();
    drawParticles();
}

function createParticles() {
    //check on every 10th tick check
    if(tick % 10 == 0) {
        if(particles.length < 100) {
            particles.push({
                    x: Math.random()*canvas.width,
                    y: 0,
                    speed: 2+Math.random()*3, //between 2 and 5
                    radius: 5+Math.random()*5, //between 5 and 10
                    color: "white",
            });
        }
    }
}

function updateParticles() {
    for(var i in particles) {
        var part = particles[i];
        part.y += part.speed;
    }
}
function killParticles() {
    for(var i in particles) {
        var part = particles[i];
        if(part.y > canvas.height) {
            part.y = 0;
        }
    }
}

function drawParticles() {
    var c = canvas.getContext('2d');
    c.fillStyle = "black";
    c.fillRect(0,0,canvas.width,canvas.height);
    for(var i in particles) {
        var part = particles[i];
        c.beginPath();
        c.arc(part.x,part.y, part.radius, 0, Math.PI*2);
        c.closePath();
        c.fillStyle = part.color;
        c.fill();
    }
}

setInterval(loop,30);

</script>
Advertisements

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s