Canvas Animation and Web Worker
Canvas Animation and Web Worker
Introduction
In this article, I will try to show one of the basic complex animations of Canvas
and Web Worker in HTML5.
We are going to draw a square and animate it around the center of canvas
. The square will make double rotation: around a circle (clockwise) and around itself (counterclockwise).
Because the user could modify the radius of the circle and the size of the square, we will create a worker to work in the background. Otherwise the JavaScript will keep forever the application threat if the user enters a bad input.
Using the Code
We start with the HTML markup. Since the Canvas
element and the Worker are not supported in the previous version of HTML, the following declaration is the one we need.
<!DOCTYPE html>
After we declare the canvas
:
<canvas id="canvas" height="400" width="400"></canvas>
We need an “Id
” because we will access it in JavaScript.
Then we need fields to allow users to interact. To keep thing simple, we create a table with one row only and three columns as follows:
<table id="table">
<tr id="table_row">
<td>
<label for="tcircle">
Circle radius</label><br />
<input type="text" id="tcircle" class="textbox" />
</td>
<td>
<label for="tsquare">
Square size</label><br />
<input type="text" id="tsquare" class="textbox" />
</td>
<td>
<input type="hidden" id="hidden" value="34,21" onchange="valueChanged()"/>
<br />
<input type="button" value="Apply" id="btn" />
</td>
</tr>
</table>
Now since our user interface is ready, let us move to JavaScript to perform drawing on the canvas
.
We start by defining two global variables:
var canvas;
var context;
The first is the canvas
and the second is the context
on which we will be working on.
Let us create our first function which draws a circle. It takes one parameter: the radius of the circle. Here is the function:
function draw_circle(radius) {
context.beginPath();
context.arc(0, 0, radius, 0, 2 * Math.PI, true);
context.closePath();
context.stroke();
}
Here is the square function:
function draw_square(size) {
// Angle between two consecutive vertices where the center of
// the repere is the center of the square.
var angle = 0;
// distance of a vertice of the center of the square.
var r = size / Math.SQRT2;
context.beginPath();
context.moveTo(r * Math.cos(angle), r * Math.sin(angle));
// angle between two consecutive vertices, which is 2*Math.PI/n.
// Here the regular polygon is a square. So n = 4.
var gama = Math.PI / 2;
// Now we make a loop to draw the tree left vertices.
for (var i = 1; i < 4; i++) {
angle += gama;
context.lineTo(r * Math.cos(angle), r * Math.sin(angle));
}
context.closePath();
context.fillStyle = "#00ff00"; // Fill it with the green color
context.fill();
}
Because we draw, clear the context
and draw again, we need one clear
function.
function clear() {
context.clearRect(0, 0, canvas.width, canvas.height);
}
The function to call when the page loads is:
function init() {
// Initialize the canvas.
canvas = document.getElementById("canvas");
// check if the canvas is supported and also of the getContext is available.
if (canvas && canvas.getContext) {
// Initialize the context.
context = canvas.getContext("2d");
// The interval of drawing is set to 10 millisecons.
return setInterval(draw, 10);
}
else {
alert("Canvas is not supported!");
}
}
Now is our draw
function.
First, let us create three others global variables for angles.
// Angle of rotation of the square around itself.
var beta = 0;
// angle of rotation around the circle.
var alpha = 0;
// angle radian make by the circle in 10 milliseconds.
var theta = .01 * Math.PI;
Now here is the draw function:
function draw() {
// We can pass parameters to the clear() function or just
// it here in order to scale when clearing.
clear();
var R = 30;
var S = 20;
alpha += theta;
context.save();
context.translate(canvas.width / 2, canvas.height / 2);
context.rotate(alpha);
//context.scale(scale, scale);
draw_circle(R);
// beta = 3.alpha. Because I rotate back the canvas first,
// then rotate it twice again in order to rotate the square.
beta -= 3 * theta;
context.save();
context.translate(R, 0);
context.rotate(beta);
draw_square(S);
context.restore();
context.restore();
}
We finish the draw and animation. Now let us allow interaction.
We start by creating our worker. It just takes the values of the two text boxes and converts them into one string
.
function applyValues(radius, size) {
return radius + "," + size;
}
/*
Add a event listener to the worker, this will
be called when the worker receives a message
from the main page.
*/
this.onmessage = function (event) {
var data = event.data;
postMessage(applyValues(data.radius, data.size));
};
To finish, in the HTML markup in the script
section, we perform the following task with some basic security and information to display to the user.
<script>
/* Check if Web Workers are supported */
function getWebWorkerSupport() {
return (typeof (Worker) !== "undefined") ? true : false;
}
if (getWebWorkerSupport() == true) {
var radius, size, message;
// var canvas=screen;
worker = new Worker("scripts/worker.js");
/*
Add an event listener to the worker, this will
be called when the worker posts a message.
*/
worker.onmessage = function (event) {
document.getElementById("hidden").value = event.data;
};
// Register event for button
document.getElementById("btn").onclick = function () {
radius = document.getElementById("tcircle").value;
size = document.getElementById("tsquare").value;
var canvas = document.getElementById("canvas");
// Check if the radius and size are numeric types
if (!isNaN(radius) && !isNaN(size)) {
// parse the radius and the size to integer
radius = parseInt(radius);
size = parseInt(size);
// Make sure that the square will never hide the center of the circle
if (radius >= size / Math.SQRT2) {
// Make sure the circle and the square will be always visible
if ((radius + (size / Math.SQRT2)) < 200) {
message = { 'radius': radius, 'size': size };
// Post the message.
worker.postMessage(message);
}
else {
alert("The summation of the radius and the half of the
hypotenuse must not be greater than 200 pixels");
}
}
else {
alert("The radius must be greater the size/sqrt(2)!");
}
}
else {
alert("Numerics type are needed!");
}
}
}
</script>
The event that wires up when the value of the hidden field changed is:
function valueChanged() {
return document.getElementById("hidden").value;
}
The hidden field is there just to update when drawing.
It is all. Thank you.
History
- 11th May, 2011: Initial version