As promised, we will now start adding features to yesterday’s particle simulation. What we wound up with was pretty basic, but is a good starting point for building on. Today, we’ll be adding several main features:
1. An emitter, which is just a point that will emit particles over time.
2. When particles reach a boundary, instead of bouncing, they will be recycled and re-emitted.
3. Rather than create all the particles at once, they’ll be created one at a time until they are all created, then just recycled.
4. Gravity.
5. Different rendering: a fill with a variable sized circle for each particle.
A decent day’s work, I think. If you want to look at the full script as you follow along, you can find it here: http://www.bit-101.com/jscanvas/mar16.js
The emitter, as just mentioned, is merely an x, y point. Here’s the beginning of the function:
[php lang=”JavaScript”]$(function() {
var points = [], numPoints = 100, i, canvas, context, width, height, gravity = 0.1, emitter;
canvas = $(“#canvas”)[0];
width = canvas.width;
height = canvas.height;
context = canvas.getContext(“2d”);
emitter = {x:width / 2, y:height};[/php]
You see we have a var for emitter, and it’s at the center of the canvas horizontally, and the bottom of the screen vertically. You can also see the var for gravity, and some of the vars from yesterday have been removed as they are no longer needed.
Then we have a function called initPoint.
[php lang=”JavaScript”] function initPoint(p) {
p.x = emitter.x;
p.y = emitter.y;
p.vx = Math.random() * 4 – 2;
p.vy = Math.random() * -5 – 3;
p.radius = Math.random() * 5 + 1;
}[/php]
This takes a point object and sets various properties on it. x and y get set to emitter.x, emitter.y. vx is random in both directions, and vy is random, but always a negative value, making particles go up. Finally, we give the point a random radius.
Now we jump to the new update function. If you’re following along on the full script file, you may notice that we’ve lost the for loop that creates all the particles. As mentioned, particles will be created one at a time a bit later.
[php lang=”JavaScript”] function update() {
var i, point, len = points.length;
for(i = 0; i < len; i += 1) {
point = points[i];
point.vy += gravity;
point.x += point.vx;
point.y += point.vy;
if(point.x > width ||
point.x < 0 ||
point.y > height ||
point.y < 0) {
initPoint(point);
}
}
}[/php]
This has gotten a bit shorter, but has a few importand additions. First, rather than looping from 0 to numPoints, we now loop from 0 to points.length. This is because to start with, there will be zero points, then 1, then 2, etc. We don't need to be trying to update scores of non-existent particles. Next change, we add gravity to point.vy. This pulls it down. Finally, instead of dealing with each boundary separately, we just see if it's hit any one, and if so, re-initialize the point. This puts it back on the emitter point, with a new random size and velocity.
Next up is the draw method, which isn't much different than before:
[php lang="JavaScript"] function draw() {
var i, point, len = points.length;
context.clearRect(0, 0, width, height);
for(i = 0; i < len; i += 1) {
point = points[i];
context.beginPath();
context.arc(point.x, point.y, point.radius, 0, Math.PI * 2, false);
context.fill();
}
}[/php]
Mainly, we just switch to using points.length instead of numPoints, and the point's radius with a black fill rather than a set radius with a stroke.
I want to jump ahead slightly, to the setInterval call:
[php lang="JavaScript"] setInterval(function() {
addPoint();
update();
draw();
}, 1000/24);[/php]
This just has one additional line, a call to a function named addPoint. And here is that function:
[php lang="JavaScript"] function addPoint() {
var point;
if(points.length < numPoints) {
point = {};
initPoint(point);
points.push(point);
}
}[/php]
This checks to see if we have reached the required number of points in the points array. If not, it creates a point object, initializes it and then pushes it into the points array. Thus, on the first frame, points will start out empty and a single point will be added. On the next frame, another one will be added, and so on until we have 100 (or whatever you set numPoints to). After that, this function will do nothing. It's probably more optimal to do the conditional in the setInterval callback, so this function is never even called, but we're working on clarity not optimization for the time being.
And that's it for today. To see it in action: http://www.bit-101.com/jscanvas/mar16.html
Also, this marks the halfway point of this series. I hope you are enjoying it and maybe learning a thing or two. Maybe even getting over a bit of JS anxiety or AS1 trauma. I know I’m sure learning a lot and having fun with it myself. Still shooting for finishing out the month. but I’ve covered a lot more ground already than I thought I would have. I still think I can dig up enough material to get through the rest of the month, bug it’s going to be a scramble as time goes on. I’m open to suggestions.
Great series of tutorials. I am new to HTML5 and canvases and come from an AS3 / Flex environment, but I find your tutorials very helpful.
Any chance of doing some mouse interaction and easing in future tutorials?
Thanks.
m( as i commented on the post from yesterday, gimme some more 🙂 this also runs fluid on the N270. absolutely.
Really nice stuff – . Since your taking suggestions, you should talk about some of the more crazy/powerful/dangerous function closure uses – it’s a confusing topic – would be refreshing to see an elegant explanation. Even basic things like this: http://jsfiddle.net/xe4R8/
Enjoying following these posts. The comparisons to Actionscript are very useful and are helping me follow and understand. Thanks a lot Keith.
cleanest example ever. Great post.