HTML 5 Canvas: An animated compass

HTML5 Animated Canvas Image

My previous posts on HTML5’s canvas implementation have simply drawn a static image; be it a thermometer, speedometer or a map.  This post blends together the various methods covered in the aforementioned posts to draw an animated compass.  This is achieved by adding two elements to the canvas.  These are the background image (a compass without its needle) and the compass needle which is continuously animated in a circular motion.

The HTML Source

The following code is stripped to the minimum in order to draw the compass:
<html lang="en">
	<head>
		<meta charset="utf-8" />
		<title>Animated Compass</title>
		<script src='compass.js'></script>
	</head>
	<body onload='init();'>
		<canvas id="compass" width="200" height="200"></canvas>
	</body>
</html>
The HTML is straight forward. The items of interest are the canvas element, the ‘onload’ event for the body and the script tag. For those who are not familiar with HTML let me explain how we draw the compass onto the canvas. When any HTML page loads the browser will run the JavaScript code attached to the onload event of the body tag e.g.
<script type="text/javascript" src="compass.js"></script>
The browser will load any JavaScript code included in a script tag prior to displaying the page to the user. Therefore, the content of my script “compass.js” will be loaded and thus the function called ‘init’ will be available. If you were to look inside the aforementioned script you will be able to see the code e.g.
function init()
{
	// Grab the compass element
	var canvas = document.getElementById('compass');

	// Canvas supported?
	if(canvas.getContext('2d'))
	{
		ctx = canvas.getContext('2d');

		// Load the needle image
		needle = new Image();
  		needle.src = 'needle.png';

  		// Load the compass image
		img = new Image();
		img.src = 'compass.png';
 		img.onload = imgLoaded;
	}
  	else
  	{
  		alert("Canvas not supported!");
  	}

}
The content of the function is quite readable and the code is ordered in logic steps. The first step is to obtain a handle to the canvas element. If the canvas element is found we then proceed to ensure that the browser supports the canvas implementation. Assuming support is implemented the second step is to load the needle and compass image resources. The second image – the compass – is created with an onload event. This event informs the browser to call the JavaScript function ‘imgLoaded’ directly after the image has loaded. This function contains the following:
function imgLoaded()
{
	// Image loaded event complete.  Start the timer
	setInterval(draw, 100);
}
The content of the function above is short and sweet! The code simply instructs the JavaScript engine to start a timer. The timer is configured to run the JavaScript function ‘draw’ every 100 milliseconds. The ‘draw’ function contains the following:
function draw()
{
	clearCanvas();

	// Draw the compass onto the canvas
	ctx.drawImage(img, 0, 0);

	// Save the current drawing state
	ctx.save();

	// Now move across and down half the
	ctx.translate(100, 100);

	// Rotate around this point
	ctx.rotate(degrees * (Math.PI / 180));

	// Draw the image back and up
	ctx.drawImage(needle, -100, -100);

	// Restore the previous drawing state
	ctx.restore();

	// Increment the angle of the needle by 5 degrees
	degrees += 5;
}
The JavaScript above first clears the canvas. The compass background image is then rendered to the canvas. The next step makes a call to the save method of the context. This saves the drawing state of canvas. The canvas drawing state is basically a snapshot of all the attributes that have been applied to the canvas including the transformations, styles, line widths and clipping regions. By default, none of these are set; thus, we are simply saving the defaults applied to our canvas so we can revert back to them later.

 

The next step deals with the animation. This involves two steps. The first step performs the following translation:

	// Now move across and down half the image
	ctx.translate(100, 100);
}
By default the rotation is performed on the canvas with the rotation centred at position 0, 0. If we were to simply ask the canvas to rotate at increments of 5 degrees the rotating needle would be drawn as follows

HTML5-rotate-without-transform

As you can see this is not adequate. In order for the needle to spin in relation to the compass background image we need to alter the centre point used for the rotation. Therefore, we set the translation to 100, 100 pixels. This happens to be the centre of our background and needle images which are 200, 200 pixels. With this set our needle now rotates as follows:

HTML5-rotate-with-transform

Once the translation and rotation settings have been applied we can then draw the needle image onto the canvas.

	// Draw the image back and up
	ctx.drawImage(needle, -100, -100);
}
It is worth pointing out that the rotation will only affect drawings made AFTER the rotate function is called; thus, the background image will not be rotated! Only the needle. The second and third parameters passed into drawImage are the x and y position that we wish to place the image. We still want the needle image drawn from origin 0,0; thus, we make the necessary adjustment to negate the translation made earlier. The combination of the translation, rotation and draw calls result in the needle being drawn at the correct angle!

The final step is to revert the drawing state, ensuring the redrawn background image is drawn as expected (with no rotation).

html5-animated-compass

A working example of the above can be seen here here. All of the source code can be obtained from my Git Hub repository here

5 thoughts on “HTML 5 Canvas: An animated compass

Leave a comment