Skip to content
May 25, 2012 / geeksretreat

HTML 5 Canvas: An animated Analogue Clock

html5-canvas-analogue-clock

HTML5′s canvas implementation allows you to draw whatever takes you fancy; be it a thermometer, speedometermap, compass or a Caesar cipher.  In this posts I’ve taken the engine used to draw the Caesar cipher and applied it to a traditional analogue clock.  This HTML5 canvas implementation draws the clock face – minus the hands – as the background.  The hour, minute and second hands are then drawn at the angle that represents the current time.  These are then redrawn every second to animate the clock arms as one would expected a real analogue clock to do!

The HTML Source

The following code is stripped to the minimum in order to draw the dial:
<html lang="en">
	<head>
		<meta charset="utf-8" />
		<title>Aanalogue Clock</title>
		<script src='analogue_clock.js'></script>
	</head>
	<body onload='init();'>
		<canvas id="clock" width="500" height="500"></canvas>
	</body>
</html>
The HTML is straight forward. The items of interest are the canvas element and the ‘onload’ event for the body. For those who are not familiar with HTML let me explain how we draw the clock 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.
	<body onload='init();'>
In order to understand how the ‘init’ function works we need to understand how the browsers locates it. The browser will load any JavaScript code included in a script tag prior to displaying the page to the user e.g.
	<script src="analogue_clock.js"></script>
Therefore, the content of the script “analogue_clock.js” will be loaded, and therefore, 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 clock element
	var canvas = document.getElementById('clock');

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

		// Load the hour hand image
		hour_hand = new Image();
  		hour_hand.src = 'hour_hand.png';

		// Load the minute hand image
		minute_hand = new Image();
  		minute_hand.src = 'minute_hand.png';

		// Load the minute hand image
		second_hand = new Image();
  		second_hand.src = 'second_hand.png';

  		// Load the clock face image
		clock_face = new Image();
		clock_face.src = 'clock_face.png';
 		clock_face.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 the browser supports the canvas. Assuming support is implemented the second step is to load the image resources. These images are the clock face and the hour, minute and second hands

hands-analogue-clock

The clock face image is created with an onload event attached. This event informs the browser to call the JavaScript function ‘imgLoaded’ directly after the image has loaded. The content of this function is:
function imgLoaded()
{
	// Image loaded event complete.  Start the timer
	setInterval(draw, 500);
}
The function above is very simple. It simply starts a timer which will be executed every 500 milliseconds (half a second). The timer instructs the browser to run the draw function. This function contains the following source code.
function draw() 
{
	var currentTime = new Date();
	
	clearCanvas();
	
	// Draw the clock onto the canvas
	ctx.drawImage(clock_face, 0, 0);
	
	// Save the current drawing state
	ctx.save();

	// Now move across and down half the 
	ctx.translate(HEGIHT/2, WIDTH/2);
 
 	rotateAndDraw(minute_hand, getRequiredMinuteAngle(currentTime));
 	
 	rotateAndDraw(hour_hand, getRequiredHourAngle(currentTime));
 	
 	rotateAndDraw(second_hand, getRequiredSecondAngle(currentTime));
	
	// Restore the previous drawing state
	ctx.restore();
	
}
The JavaScript above first clears the canvas. The clock face image is then rendered to the canvas to act as a background. 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 
    ctx.translate(HEIGHT/2, WIDTH/2);
By default the rotation is performed on the canvas with the rotation centred at position 0, 0. As we require the hands of the clock to rotate around the centre of the clock we move first set the centre position to the centre point of the clock. Thus, we use two variable called HEIGHT and WIDTH. These are set in a global scope at the top of the JavaScript file:
var HEIGHT= 500;
var WIDTH = 500;
The value applied to the global variables match the size of the clock face and hand images. Thus, the halfway point for both the height and width equates to the centre point of the images. Once the centre point has been computed and set we can then draw the clock’s hands.
 	rotateAndDraw(minute_hand, getRequiredMinuteAngle(currentTime));
 	
 	rotateAndDraw(hour_hand, getRequiredHourAngle(currentTime));
 	
 	rotateAndDraw(second_hand, getRequiredSecondAngle(currentTime));
Each hand is drawn by calling the ‘rotateAndDraw’ function. This function takes two parameters. The first is the image to rotate, followed by the angle to rotate. Each of the three secondary parameters are the result of a further function calls. Theses three functions accept the current date and time as a parameter and convert the minute, hour or second unit of the current time to the corresponding angle. These function include the following:
function getRequiredMinuteAngle(currentTime)
{
	// Calculate the expected angle
	return Math.floor(((360/60) * currentTime.getMinutes()),0);
}

function getRequiredHourAngle(currentTime)
{
	// Calculate the expected angle
	return Math.floor(((360/12) * currentTime.getHours()),0);
}

function getRequiredSecondAngle(currentTime)
{	
	// Calculate the expected angle
	return Math.floor(((360/60) * currentTime.getSeconds()),0);
}
Each of the three functions above simply multiplies the angle that represents one minute (360 divided by 60) , one hour (360 divided by 12) or one second (360 divided by 60), by the corresponding value obtained from the current date and time. The output of which is passed to the ‘rotateAndDraw’ function:
function rotateAndDraw(image, angle)
{
	// Rotate around this point
	ctx.rotate(angle * (Math.PI / 180));
 
	// Draw the image back and up
	ctx.drawImage(image, 0-HEIGHT/2, 0-WIDTH/2);
	
	ctx.rotate(-angle * (Math.PI / 180));
}
The first step in the ‘rotateAndDraw’ function is to rotate the canvas context. It is worth pointing out that the rotation will only affect drawings made AFTER the rotate function is called; thus, the clock face will not be rotated! Only the hands. Each hand is drawn by rotating the drawing context to the correct angle and then drawing the image onto the canvas. The rotation is then reversed so that the angle is reverted back to zero, which ensure the next hand can be drawn using the same theory.

html5-canvas-analogue-clock

That concludes this post. A working example of this post can be view here here. Full credit for the base image goes to this site for providing the PSD that inspired me to create the HTML5 clock. The code and my previous HTML canvas projects are now available on Git Hub https://github.com/rheh/HTML5-canvas-projects/tree/master/
About these ads

4 Comments

Leave a Comment
  1. JOHN / Mar 19 2014 13:21

    Nice clock – and it does just what I wanted except that the hour hand only moves on the hour so there are times when the clock appears almost an hour slow. The solution is to add one twelfth of the minute hand angle to the getRequiredHourAngle(currentTime) fubction:

    // Calculate the expected angle
    return Math.floor(((360 / 12) * currentTime.getHours() + (getRequiredMinuteAngle(currentTime)/12)), 0);
    }

    Hope it works for you.

    JOHN

    • geeksretreat / Mar 19 2014 21:04

      Excellent observation – I’ll add that! Thanks.

      • JOHN / Mar 19 2014 21:11

        Thanks – our web site has a Flash clock but this does not work on mobiles etc so we are re-vamping the site soon. A trial page is at:
        http://www.colour.org.uk/web-new/index.html
        but the text is holding text and no links yet work – but your clock does. Great.

        JOHN

      • geeksretreat / Mar 19 2014 21:28

        Nice to see my code being used! Good luck with the revamp

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s

Follow

Get every new post delivered to your Inbox.

Join 159 other followers

%d bloggers like this: