The complete program can also be found and tried on the I/O Press website: www.iopress.info.
<!DOCTYPE html>
<html>
<head>
<title>JavaScript Graphics</title>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width,
initial-scale=1.0">
</head>
<body>
<input id="fps">
<script>
function createCanvas(h, w) {
var c = document.createElement("canvas");
c.width = w;
c.height = h;
return c;
}
function Pos(x, y) {
this.x = x;
this.y = y;
}
function Vel(x, y) {
this.x = x;
this.y = y;
}
function Acc(x, y) {
this.x = x;
this.y = y;
}
function Ball(pos, vel, acc, r) {
this.pos = pos;
this.vel = vel;
this.acc = acc;
this.update = function () {
this.pos.x += this.vel.x;
this.pos.y += this.vel.y;
this.vel.x += this.acc.x;
this.vel.y += this.acc.y;
if (this.pos.x + r > ctx.canvas.width) {
this.pos.x = ctx.canvas.width - r;
this.vel.x = -this.vel.x;
}
if (this.pos.y + r > ctx.canvas.height) {
this.pos.y = ctx.canvas.height - r;
this.vel.y = -this.vel.y;
}
if (this.pos.x - r < 0) {
this.pos.x = r;
this.vel.x = -this.vel.x;
}
if (this.pos.y - r < 0) {
this.pos.y = r;
this.vel.y = -this.vel.y;
}
};
this.render = function () {
var path = new Path2D();
path.arc(this.pos.x, this.pos.y, r, 0, 2 * Math.PI);
ctx.fill(path);
};
}
Animation = {};
Animation.spriteList = [];
Animation.clearCanvas = function () {
ctx.clearRect(0, 0, ctx.canvas.width, ctx.canvas.height);
};
Animation.run = function (t) {
Animation.frameRate(t);
Animation.clearCanvas(t);
for (var i = 0;i < Animation.spriteList.length; i++) {
Animation.spriteList[i].update();
Animation.spriteList[i].render();
}
requestAnimationFrame(Animation.run);
};
Animation.frameRate = function (t) {
if (typeof t !== "undefined") {
Animation.frameRate.temp = 0.8 * Animation.frameRate.temp +
0.2 * (t - Animation.frameRate.tp);
Animation.frameRate.tp = t;
}
Animation.frameRate.count++;
if (Animation.frameRate.count === 120) {
fps.value = (1000 / Animation.frameRate.temp).toFixed(2);
Animation.frameRate.temp = 0;
Animation.frameRate.count = 0;
}
};
Animation.frameRate.count = 0;
Animation.frameRate.tp = 0;
Animation.frameRate.temp = 0;
var ctx = document.body.appendChild( createCanvas(600, 600)).getContext("2d");
var noBalls = 3;
var balls = [];
for (i = 0; i < noBalls; i++) {
balls[i] = new Ball(
new Pos(Math.floor(Math.random() * 250),
Math.floor(Math.random() * 250)),
new Vel(Math.floor(Math.random() * 10) – 5,
Math.floor(Math.random() * 10) - 5),
new Acc(0, 0.1), 20);
}
Animation.spriteList = balls;
Animation.run();
</script>
</body>
</html>
In book but not in this extract:
Using BitBlt
Raw Animation
Summary
The fundamental way of getting bitmaps into a web page is to use the <img> tag or the Image object.
Loading an image is always asynchronous. There is no way to pause and wait for an image to load, no matter how hard you try.
The only way to handle asynchronous image loading is to use the onload event handler.
A more modern approach is to wrap the event handler in a function that returns a Promise. The function can then be used with async/await to make the asynchronous operation look perfectly like a synchronous load.
Once you have an image loaded, you can use the drawImage method to draw the pixels to a canvas.
ImageBitmap is the new and faster way to source a bitmap for a canvas.
Animation can be achieved by repeated drawing of the animated object or by drawing a bitmap into a new location – BitBlt. In most cases blitting is faster.
You can arrange to take into account the time between frames to determine how far an object should have moved.
Animation is often best organized around the idea of a sprite, a shape with position, velocity and acceleration.
Sprites are perfect for implementation using object-oriented methods. Doing this loses about 1/3 of the speed you can achieve using a non-object-oriented, direct implementation.
Now available as a paperback or ebook from Amazon.