HTML5 gives us a wide range of possibilities to bring power of Java applet and OpenGL graphics to
a world of web pages. This article demonstrates how to create a program
Particle Fireworks to simulate
falling bodies with help of object oriented JavaScript, JQuery and HTML5 canvas element. To change the amount of spawns flying
bodies slide the handler on the left of the screen. To stop or start simulation click at the button on the left top corner. The source code is available at
https://github.com/serjio28/ParticleFireworks
1.Physics of falling bodies
Before start programming, let's consider the physics of a falling body and prepare formulas
to simulate body motions. Picture #1 outlines the forces which impact on the falling body.
To make things simple we will consider only gravity force F=-mg, where g is a constant 9.82.
The force impacts along the backward direction regarding axe Y and therefore has a negative sign.
In according to the second Newton law let's write formulas of the forces which impacts on the body
in regarding to the each axe.
1.Acceleration is a second derivative of the distance so we can write for axe Y:
2. Do the first integration. C0 - is an initial velocity
3. Do the second integration. C1 - is an initial displacement on axe y
4. Since C0 is a vector we need to find out a projection of the initial velocity to the axe Y
5. Write out a final formula we will use to find position of the object on axe y
6. Write the second Newton law for the forces impact on axe X
7. Do the first integration
8. Do the second integration
9. Find projection of the initial velocity to axe X
10. Write down a final formula to find position of the object on axe X
11. Finally outline the both formulas we will use to find position of the object
2. Programming
Following a paradigm of the object oriented programming we define a class that will
keep all necessary methods and properties of a single body. First at all define a
constructor that will accept the resolution of the current window, canvas descriptor,
the initial position of the body, trace length, initial velocity and angle.
function MotionObject(H,W, GraphCanvas, Radius, X, Y, path_max, v0, alfa, g) {
...
}
Next, define the most important method we ever needed here. This one will draw the current
object on the canvas. Notice the fact, that the function uses this.context to refer a canvas
object.
MotionObject.prototype.draw = function(color,x,y,r) {
this.context.fillStyle = color;
this.context.strokeStyle= color;
this.context.beginPath();
this.context.arc(x,y,r, 0, Math.PI * 2, true);
this.context.closePath();
this.context.fill();
this.context.stroke();
}
Let's remember the formulas #11 from the previous part and write them in JavaScript.
MotionObject.prototype.Xmove = function() {
this.x= this.xShift + Math.ceil(this.v0 * Math.cos(this.alfa))* this.t*this.time_divider;
}
MotionObject.prototype.Ymove = function() {
this.y= this.H - Math.ceil( this.v0 * Math.sin(this.alfa)* this.t*this.time_divider - (this.g * this.t* this.t)/2 );
}
An important detail of the planned program is to ability to simulate as many objects as it is possible simultaneously.
Therefore each object will be given a piece of a time to live in the universe of the our
program. Define it as a method life where object must set its current position and draw itself
MotionObject.prototype.life = function() {
this.move();
this.paint(this.x,this.y, this.r);
}
The class we have made can locate its position on any given time according to the physics law we defined and draw itself. But it cannot live alone.
To help it, we need to create a place where it will resides. So now the time to define another class GraphCanvas. Its core aims are to define canvas,
handle two independent threads - one to insert new flying bodies and another to give each of them a piece of the time to render.
function GraphCanvas(d, rlimit) {
...
}
Notice that there is another hidden canvas behind the main one. Each object will render itself on the hidden canvas and once all of them are finished
the contents of this canvas is copied to the main one. This way we will optimize the process of rendering and eliminate jerking.
GraphCanvas.prototype.createCanvas = function() {
var GraphCanvasObject = this;
...
var canvas_definition = ["<canvas width='" + this.WIDTH + "' height='" + this.HEIGHT+ "' style=\"display:none\"><canvas>",
"<canvas width='"+ this.WIDTH+ "' height='"+ this.HEIGHT+ "' style=\"z-index:1;position: absolute; padding-left: 0;padding-right: 0;margin-left: auto; margin-right: auto;\"></canvas>" ];
for ( var i = 0; i < canvas_definition.length; i++) {
try {
// initialize a canvas
var canvas = $(canvas_definition[i]);
// get context and graph
this.canvasContext[i] = canvas.get(0).getContext("2d");
this.canvasGraph[i] = canvas.get(0);
// add the context to body of the document
canvas.appendTo('body');
} catch (e) {
var message = e.message;
var name = e.name;
console.log(" name:" + name + " message:" + _message);
return false;
}
}
return true;
};
GraphCanvas provides two independent threads to spawn new flying object and to allow them to render themselves.
The both are started on the method GraphCanvas.launch() and running until GraphCanvas.terminate() is invoked.
Here the first thread starts and call GraphCanvas.life 25 times per second.
GraphCanvas.prototype.launch = function() {
var GraphCanvasObject = this;
this.hAnimation = setInterval(function() {
GraphCanvasObject.life();
}, Math.ceil(1000 / 25));
....
Another thread is to spawn new flying object. Notice that its frequency depends on an user choice that is sent as
a value of the variable this.ball_frequency. The code generates a flying object launched with a random initial speed
that must exceed 5 and angle that must be higher than PI/6 and lower 5*PI/6.
...
this.hObjectGen = setInterval(function() {
if(GraphCanvasObject.mutex == 0) {
GraphCanvasObject.mutex = 1;
var x = 100;
var angle = Math.random() * (Math.PI);
var v0 = Math.random() * 20;
if (v0 > 5 && angle > Math.PI/6 && angle < 5*Math.PI/6) {
var Obj = new MotionObject(GraphCanvasObject.HEIGHT, GraphCanvasObject.WIDTH,
GraphCanvasObject.canvasContext[1], 1, GraphCanvasObject.w_middle, GraphCanvasObject.HEIGHT - 10, 5, v0, angle,9.82);
if(Obj!=undefined) GraphCanvasObject.addObject(Obj);
};
GraphCanvasObject.mutex = 0;
};
}, this.ball_frequency);
};
The core of the class is a function life(). It makes alive each object we simulate. First at all, it rejects and terminates
the objects which position exceeded our screen size. Next, it iterates through the list of the all running objects and
give each of them a piece of the time to make them moving.
GraphCanvas.prototype.life = function() {
var deadlock_detect = 0;
if (this.mutex == 0) {
this.mutex = 1;
if (this.Inhabitans.length == 0) {
this.terminate();
};
for ( var i = 0; i < this.Inhabitans.length; i++) {
var item = this.Inhabitans[i];
if (item != undefined) {
if (item.isDied()) {
delete this.Inhabitans[i];
this.Inhabitans.splice(i, 1);
};
};
}
var r = "";
this.Inhabitans.forEach(function(item) {
if (item != undefined){
item.life();
};
});;
this.mutex = 0;
};
this.canvasContext[1].drawImage(this.canvasGraph[0], 0, 0);
};
Finally write a start up code to instantiate objects from the defined classes and start the simulation
var GC = new GraphCanvas(2, 500);
if (GC.createCanvas()) {
var Obj = new MotionObject(GC.HEIGHT, GC.WIDTH, GC.canvasContext[1], 1,
GC.w_middle, GC.HEIGHT - 10, 5, 10, Math.PI / 4, 9.82);
GC.addObject(Obj);
GC.launch();
}
Now start the demo and enjoy with
Particle fireworks
The source code is available at
https://github.com/serjio28/ParticleFireworks