Tuesday, January 17, 2012

Tutorial: Simple Clock Animation with HTML5



In this tutorial I will show you how to create a simple clock and animate it.

This tutorial is split in three steps:
  • Step 1: Create the clock's background.
  • Step 2: Initialize the current clock time.
  • Step 3: Animate the clock.
Hint: This tutorial assumes that you have a certain knowledge of HTML5 and JavaScript.

We begin this tutorial by creating the background of this clock.

The following html start code helps you initialize the canvas tag and its context that you require for your animation.


<!DOCTYPE html>

<head>
<meta content="text/html; charset=utf-8" http-equiv="Content-Type" />
<title>Simple Clock Animation</title>
<script type="application/javascript">

var canvas; // the clock canvas
var ctx; // canvas’s context

// clock settings
var centerX = 0;
var centerY = 0;
var radius = 150;

function init() {

canvas = document.getElementById("clock");
ctx = canvas.getContext("2d");

centerX = canvas.width / 2;
centerY = canvas.height / 2;

drawClock();

}

</script>
</head>

<body onload="init()">

<canvas id="clock" height="500" width="500">
Your&nbsp;browser&nbsp;does&nbsp;not&nbsp;support&nbsp;HTML5</canvas>

</body>

</html>

Step 1: will consist of drawing the clock. This is given by the function drawClock().

function drawClock() {
                drawClockBackground();
}

The function drawClockBackground() inside the drawClock() function will draw the background of the clock. It will create small lines around the center (centerX, centerY) of the clock which is also the center of the canvas.

We start creating the lines from the right to the left, and using the function drawArcAtPosition() located below. Also, we will highlight the main lines.

At final, a small circle is added at the center of the clock (see function drawLittleCircle()).

Hint: You can also use a predefined image (if you like) instead of drawing the background of the clock yourself.

function drawClockBackground() {

var correction = 1/300;
var shift_unit = 1/170;
var shift_factor = 1/30;
var angle_initial_position = 2;
var angle_current_position_begin = 0;
var angle_current_position_end = 0;
var repeat = 60;
var lineWidth = 10;

for (var i=0; i < repeat; i+=1) {

angle_current_position_begin = angle_initial_position - (i * shift_factor) - correction;
angle_current_position_end = angle_current_position_begin + shift_unit;

if (i % 5 == 0) lineWidth = 20;
else lineWidth = 10;

drawArcAtPosition(centerX, centerY, radius, 
angle_current_position_begin*Math.PI
angle_current_position_end*Math.PI, false, lineWidth);

}

drawLittleCircle(centerX, centerY);

}

function drawArcAtPosition(centerX, centerY, radius, 
start_angle, end_angle, counterclockwise, lineWidth) {
ctx.beginPath();
ctx.arc(centerX, centerY, radius, start_angle, end_angle, counterclockwise);
ctx.lineWidth = lineWidth;
ctx.strokeStyle = "black";
ctx.stroke();
ctx.closePath();
}

function drawLittleCircle(centerX, centerY) {
drawArcAtPosition(centerX, centerY, 4, 0*Math.PI, 2*Math.PI, false, 4);
}

Afterward, we create the Hands for seconds, minutes, and hours. The below figure show the hand for the seconds.

// draw Hand for seconds
function drawSecondsHand() {
endX = centerX + radius*Math.sin(seconds*Math.PI / 30);
endY = centerY - radius*Math.cos(seconds*Math.PI / 30);
drawHand(centerX, centerY, endX, endY);
}

function drawHand(beginX, beginY, endX, endY) {
ctx.beginPath();
ctx.moveTo(beginX, beginY);
ctx.lineTo(endX, endY);
ctx.stroke();
ctx.closePath();
}

The same principle is applied for minutes and hours. The hands will be a little bit smaller than that of the seconds-Hand. The minutes-Hand will move depending on the seconds-Hand, and the hours-Hand will move depending on the minutes-Hand. The moving of the Hands will be constant and smooth.

// draw Hand for minutes
function drawMinutesHand() {

var rotationUnit = minutes + seconds / 60;
var rotationFactor = Math.PI / 30;
var rotation = rotationUnit*rotationFactor;
var handLength = 0.8*radius;

endX = centerX + handLength*Math.sin(rotation);
endY = centerY - handLength*Math.cos(rotation);
drawHand(centerX, centerY, endX, endY);
}

// draw Hand for hours
function drawHoursHand() {

var rotationUnit = 5*hours + minutes / 12;
var rotationFactor = Math.PI / 30;
var rotation = rotationUnit*rotationFactor;
var handLength = 0.4*radius;

endX = centerX + handLength*Math.sin(rotation);
endY = centerY - handLength*Math.cos(rotation);
drawHand(centerX, centerY, endX, endY);
}

The function drawClock() will be adequately updated as follow, where we add the Hands for the clock.

function drawClock() {
                drawClockBackground();
                drawSecondsHand();
                drawMinutesHand();
                drawHoursHand();
}

Step 2: will consists of animating the Hands. However, before we start we have to get the current time of the system. Thus, we create 4 additional system variables as follows:

// time settings
var date;
var hours;
var minutes;
var seconds;

And, we create a function called initTime(), where we initialize the data object and pass to our variables the current time of our system and clock.

// get the system time
function initTime() {
date = new Date();
hours = date.getHours() % 12;
minutes = date.getMinutes();
seconds = date.getSeconds();
}

Now, add the initTime() function before the drawClock() function in the init() function. This is because the Hands will be drawn according to the system current time.

function init() {
initTime();
drawClock();
}

Step 3: will consist now of animating the Hands(seconds, minutes, and hours) of the clock.
We are going now to create an animate clock function called animateClock() as follows:

// animate clock
function animateClock() {

clearCanvas();
refreshTime();
drawClock();

}

Inside this function we have three functions named: clearCanvas(), refreshTime(), and drawClock().
The first function will clear the canvas for the animation. The second function is going to refresh the current elapsed time, and the final function, which we have already seen, is going to redraw the clock with its Hands according to the refreshed time.

// clear canvas
function clearCanvas() {
ctx.clearRect(0, 0, canvas.width, canvas.height);
}

// refresh time after 1 seconde
function refreshTime() {
seconds += 1;
if (Math.floor((seconds / 60)) != 0) { minutes += 1; seconds %= 60; }
if (Math.floor((minutes / 60)) != 0) { hours += 1; minutes %= 60; }
}

// draw or redraw Clock after time refresh
function drawClock() {
                drawClockBackground();
                drawSecondsHand();
                drawMinutesHand();
                drawHoursHand();
}

In order to animate the clock we are going now to add the setInterval(..) function inside the init() function which will animate the clock every second. Of course, the function that will be called every second is the animateClock() function.

function init() {

canvas = document.getElementById("clock");
ctx = canvas.getContext("2d");

centerX = canvas.width / 2;
centerY = canvas.height / 2;

initTime();
drawClock();

setInterval("animateClock()", 1000);

}

The full clock's code can be viewed below:

<!DOCTYPE html>

<head>
<meta content="text/html; charset=utf-8" http-equiv="Content-Type" />
<title>Simple Clock Animation</title>
<script type="application/javascript">

var canvas; // the clock canvas
var ctx; // canvas's context

// clock settings
var centerX = 0;
var centerY = 0;
var radius = 150;

// time settings
var date;
var hours;
var minutes;
var seconds;

function init() {

canvas = document.getElementById("clock");
ctx = canvas.getContext("2d");

centerX = canvas.width / 2;
centerY = canvas.height / 2;

initTime();
drawClock();

setInterval("animateClock()", 1000);

}

// get the system time
function initTime() {

date = new Date();
hours = date.getHours() % 12;
minutes = date.getMinutes();
seconds = date.getSeconds();

}

// animate clock
function animateClock() {

clearCanvas();
refreshTime();
drawClock();

}

// clear canvas
function clearCanvas() {
ctx.clearRect(00, canvas.width, canvas.height);
}

// refresh time after 1 seconde
function refreshTime() {
seconds += 1;
if (Math.floor((seconds / 60)) != 0) { minutes += 1; seconds %= 60; }
if (Math.floor((minutes / 60)) != 0) { hours += 1; minutes %= 60; }
}

// draw or redraw Clock after time refresh
function drawClock() {
                drawClockBackground();
                drawSecondsHand();
                drawMinutesHand();
                drawHoursHand();
}

function drawHand(beginX, beginY, endX, endY) {
ctx.beginPath();
ctx.moveTo(beginX, beginY);
ctx.lineTo(endX, endY);
ctx.stroke();
ctx.closePath();
}

// draw Hand for seconds
function drawSecondsHand() {
endX = centerX + radius*Math.sin(seconds*Math.PI / 30);
endY = centerY - radius*Math.cos(seconds*Math.PI / 30);
drawHand(centerX, centerY, endX, endY);
}


// draw Hand for minutes
function drawMinutesHand() {
var rotationUnit = minutes + seconds / 60;
var rotationFactor = Math.PI / 30;
var rotation = rotationUnit*rotationFactor;
var handLength = 0.8*radius;

endX = centerX + handLength*Math.sin(rotation);
endY = centerY - handLength*Math.cos(rotation);
drawHand(centerX, centerY, endX, endY);
}

// draw Hand for hours
function drawHoursHand() {
var rotationUnit = 5 * hours + minutes / 12;
var rotationFactor = Math.PI / 30;
var rotation = rotationUnit*rotationFactor;
var handLength = 0.4*radius;

endX = centerX + handLength*Math.sin(rotation);
endY = centerY - handLength*Math.cos(rotation);
drawHand(centerX, centerY, endX, endY);
}

function drawClockBackground() {

var correction = 1/300;
var shift_unit = 1/170;
var shift_factor = 1/30;
var angle_initial_position = 2;
var angle_current_position_begin = 0;
var angle_current_position_end = 0;
var repeat = 60;
var lineWidth = 10;

for (var i=0; i < repeat; i+=1) {

angle_current_position_begin = angle_initial_position - (i * shift_factor) - correction;
angle_current_position_end = angle_current_position_begin + shift_unit;

if (i % 5 == 0) lineWidth = 20;
else lineWidth = 10;

drawArcAtPosition(centerX, centerY, radius, 
angle_current_position_begin*Math.PI
angle_current_position_end*Math.PI, false, lineWidth);

}

drawLittleCircle(centerX, centerY);

}

function drawArcAtPosition(centerX, centerY, radius, start_angle, 
end_angle, counterclockwise, lineWidth) {
ctx.beginPath();
ctx.arc(centerX, centerY, radius, start_angle, end_angle, counterclockwise);
ctx.lineWidth = lineWidth;
ctx.strokeStyle = "black";
ctx.stroke();
ctx.closePath();
}

function drawLittleCircle(centerX, centerY) {
drawArcAtPosition(centerX, centerY, 4, 0*Math.PI, 2*Math.PI, false, 4);
}

</script>
</head>

<body onload="init()">

<canvas id="clock" height="500" width="500">
Your&nbsp;browser&nbsp;does&nbsp;not&nbsp;support&nbsp;HTML5</canvas>

</body>

</html>

2 comments: