JavaScript analog clock

1 post Page 1 of 1
Contributors
User avatar
comathi
Coding God
Coding God
Posts: 1242
Joined: Fri Mar 26, 2010 1:59 pm

JavaScript analog clock
comathi
Hi everybody :)

It's been a while since I've written a JavaScript tutorial, so today I thought I'd talk about clocks. I was inspired to write this tuturial after the "Fun Time" Mad March challenge Cody posted a few days ago.

My entry for that challenges isn't a conventionnal analog clock, but it uses a lot of the same math as a real analog clock would, so the code I'll show you today is similar in many ways to the one I used.

Image

The boring stuff
First of all, let's get the HTML and CSS out of the way. We're going to try to make the clock pretty, but we're not going to spend a lot of time going over aesthetics.

There's barely any HTML involved, so I'll just copy and paste the whole code below:
Code: Select all
<!DOCTYPE HTML>
<html lang="en">
    <head>
        <title>JavaScript Clock</title>
        <link rel="stylesheet" type="text/css" href="style.css">
        <link href='http://fonts.googleapis.com/css?family=Roboto:400,700' rel='stylesheet' type='text/css'>
        <script type="text/javascript" src="engine.js"></script>
    </head>
    <body>
        <div id="title">
            <h2>JavaScript clock</h2>
        </div>
        <div id="wrapper">
            <canvas id="clockface" height="500" width="500"></canvas>
        </div>
        <div id="copyright">
            <p>Design and code by <a href="http://www.comathi.com">comathi</a></p>
            <p>Go crazy!</p>
        </div>
    </body>
</html>
This is pretty basic. We have a link to a stylesheet, which we'll go over shortly, as well as a link to Google's Roboto font (not necessary, but it does add a bit of style to our clock :) ).

The really important part of the above code is our canvas. Everything you see on the clock will be drawn onto it.

The CSS isn't much complicated either. I'll post it in little chunks and explain it as I go:
Code: Select all
/*Reset*/
* {
    margin: 0;
    padding: 0;
    font-family: 'Roboto', sans-serif;
    color: #fff;
}
This is just a very basic reset, to make sure our browser doesn't add margins or padding to objects without we wanting to. It also sets the default font for the page (Roboto), although you can change that to whatever you want.
Code: Select all
body
{
    background: url('bg.png');
}

#title
{
    margin-top: 25px;
}

#title h2
{
    text-align: center;
}
We'll be using this image as the background pattern:

Image

Other than that, we're just positionning our title.
Code: Select all
#wrapper
{
    width: 500px;
    
    border: 10px solid rgba(33,96,182,0.7);
   
    -webkit-border-radius: 50%;
    -moz-border-radius: 50%;
    border-radius: 50%;
    
    margin: 50px auto 0 auto;
}

#clockface
{
    height: 500px;
    width: 500px;
}
The last part of the CSS code is the most important. It sizes and positions both our wrapper (the clock's container), as well as the clock itself (the canvas). Again, most of this is just to make it all pretty, but remember the clock will be 500 pixels wide by 500 pixels high.

You can now save the file as style.css in the same folder as your HTML file (which you can name however you want) so that they both link together.

The fun part
Hurray! Now that we're done with all that boring HTML and CSS, let's get to the actual clock script!

The first thing we'll want to do is define some Objects. To make moving our clock hands around easier, we'll define each hand as a seperate Object, with its own set of properties:
Code: Select all
var hourHand = new Object();
    hourHand.x1 = 250;
    hourHand.y1 = 250;
    hourHand.x2 = 250;
    hourHand.y2 = 150;
    hourHand.length = 100;

var minuteHand = new Object();
    minuteHand.x1 = 250;
    minuteHand.y1 = 250;
    minuteHand.x2 = 250;
    minuteHand.y2 = 100;
    minuteHand.length = 150;

var secondHand = new Object();
    secondHand.x1 = 250;
    secondHand.y1 = 250;
    secondHand.x2 = 250;
    secondHand.y2 = 50;
    secondHand.length = 200;
As you can see, each hand has its first point (x1, y1) set at the center of our clock. Since the clock is 500 x 500 pixels in size, that center happens to be at (250,250). For now, we've also placed the second point (x2, y2) at a 90 degree angle above the center. Each hand has a different length, so we can tell them apart when we look at the clock.

The last set of variables we want to declare are the following:
Code: Select all
//Canvas and context
var c, cont;

//Angles
var hA, mA, sA;
c and cont will be used for the clockface (canvas) and its context respectively. The context of the canvas allows us to draw on it. We also declared three diffent angles, hA, mA and sA, for the hours hand, the minutes hand and the seconds hand.

We're writing this script bottom down, so the first function we encounter is drawclock(). As the name suggests, this function will be used to draw everything onto our canvas.

Start by adding an empty function to your script:
Code: Select all
function drawclock()
{

}
Inside the function, we'll first want to clear (erase) everything that's on the clock, so we can start fresh:
Code: Select all
//Clear the clock
cont.clearRect(0, 0, c.width, c.height);
Once that's done, we can begin drawing out clock hands. Let's start with the hours:
Code: Select all
//Draw the hour hand
cont.strokeStyle = "#FF0000";
cont.beginPath();
cont.moveTo(hourHand.x1, hourHand.y1);
cont.lineTo(hourHand.x2, hourHand.y2);
cont.lineWidth = 15;
cont.stroke();
As you can see, we set a colour for our hand (in the hours' case, we want the hand to be red). We then begin to draw our path and the line that will represent the hand with the moveTo() and lineTo() functions. You can probably guess what lineTo() does. moveTo(), however, moves our "pencil" to the specified point. lineTo, on the other hand, will draw a line from the point we moved to all the way to the point we passed as parameter.

We also want to make the line thicker, 15 pixels in this case.

The code for both the minutes and the seconds hands does not really differ from the hours code. We simply change the colour and line width:
Code: Select all
//Draw minute hand
cont.strokeStyle = "#FFFFFF";
cont.beginPath();
cont.moveTo(minuteHand.x1, minuteHand.y1);
cont.lineTo(minuteHand.x2, minuteHand.y2);
cont.lineWidth = 5;
cont.stroke();
    
//Draw the second hand
cont.strokeStyle = "#FFFFFF";
cont.beginPath();
cont.moveTo(secondHand.x1, secondHand.y1);
cont.lineTo(secondHand.x2, secondHand.y2);
cont.lineWidth = 3;
cont.stroke();
Just to add a little bit of style to our clock, we'll also add a small circle in the center, above our hands:
Code: Select all
//Draw the center cirle
cont.fillStyle = "#00FF00";
cont.beginPath();
cont.arc(250, 250, 15, 0, Math.PI * 2, true);
cont.fill();
The code is similar to the one used for the hands. The main difference, however, is that we use the arc() function instead of moveTo() and lineTo(). Arc let's us draw an arc or a full circle with the following parameters: x position, y position, radius, start point, end point, direction. We want the circle to be centered, so we set it's location to (250,250) (remember, the clock is 500 x 500 pixels in size.). We also want a full circle, so we'll set the starting point at 0 and the end point at 2 PI. Angles in most programming languages, including JavaScript, are calculated in radians. A radian is the measure of an angle intercepting an arc the length of the radius. By definition, a circl's circumference is 2 PI times the radius. A circle can therefore be divided into 2 PI radians. If you're still confused, here's a diagram to show you:

Image

The last part of our drawing function is to draw the little lines you usually see all around a clock to indicate the time. This bit has a little more math involved than the rest, but if you're familiar with basic trigonometry, you'll have no problem understanding it :)
Code: Select all
//Draw the ticks
for (var i=1; i<13; i++)
{
    var x1 = 250;
    var x2 = 250;
    var y1 = 25;
    var y2 = 0;
    
    cont.strokeStyle = "#FFFFFF";
    cont.beginPath();
    cont.moveTo(250 + 225 * Math.cos((2 * Math.PI) * (i / 12)), 250 + 225 * Math.sin((2 * Math.PI) * (i / 12)));
    cont.lineTo(250 + 250 * Math.cos((2 * Math.PI) * (i / 12)), 250 + 250 * Math.sin((2 * Math.PI) * (i / 12)));
    cont.lineWidth = 3;
    cont.stroke();
}
Basically, we're drawing the same line 12 times (hence the loop), but with 12 different rotation values. We want 12 lines total, so each line is seperated from the previous line by an angle of 2 PI / 12 radians. From there, it's easy to calculate the line's position using the sine and cosine trigonometric functions.

That's all for our drawing function. We can now move on to the actual clock moving function, clockwork():
Code: Select all
function clockwork()
{

}
The first thing we need to do inside this function is get the time. This is done using JavaScript's Date object. We can them put the hours, minutes and seconds into three different variables:
Code: Select all
var time = new Date ( );
  
//Get the current hour, minute and second
var h = time.getHours();
var m = time.getMinutes();
var s = time.getSeconds();
Next up is calculating the angles for each hand. Logically, the angle we need for a given hour is 1/12 of the circle, times the number of hours. With radians, this is easy. We simply want 1/12 radians times the number of hours. Minutes and seconds are also easy, but we're using 1/60 of the circle's circumference instead:
Code: Select all
hA = (2 * Math.PI) * (h / 12) - (2 * Math.PI / 4);
    
mA = (2 * Math.PI) * (m / 60) - (2 * Math.PI / 4);
    
sA = (2 * Math.PI) * (s / 60) - (2 * Math.PI / 4);
The only odd thing about the above code is that we had to offset each angle by 90 degrees (2 PI / 4 radians). This is just because of how JavaScript's trigonometric angle is defined.

Once we know all our angles, we can calculate the position of the hands, again using the sine and cosine functions:
Code: Select all
//Change the hour hand's location
hourHand.x2 = hourHand.x1 + hourHand.length * Math.cos(hA);
hourHand.y2 = hourHand.y1 + hourHand.length * Math.sin(hA);

//Change the minute hand's location
minuteHand.x2 = minuteHand.x1 + minuteHand.length * Math.cos(mA);
minuteHand.y2 = minuteHand.y1 + minuteHand.length * Math.sin(mA);

//Change the second hand's location
secondHand.x2 = secondHand.x1 + secondHand.length * Math.cos(sA);
secondHand.y2 = secondHand.y1 + secondHand.length * Math.sin(sA);
Once we have all our angles and have calculated the position of the hands, we can call the drawing function:
Code: Select all
drawclock();
The last function in our code is the mandatory init() function. This is called at the beginning of our script, and we'll use it to set some of our global variables, as well as set the timer for the clock:
Code: Select all
function init()
{
    c = document.getElementById("clockface");
    cont = c.getContext("2d");
    
    clockwork();
    
    setInterval(function(){clockwork();},1000);
}
Here's something that might be new to you: setInterval(). This allows us to call a function every so often (in our case, every 1000 milliseconds, or every second). We're using it to call the clockwork() function and update our hands' positions.

That's all for the init function. There's just one last line of code we want to add at the bottom of our script:
Code: Select all
onload = init;
This will ensure the init() function is called when our script loads.

You can now save the script as engine.js in the same folder as both the CSS and HTML files, to make sure they all link up together.

That's it! You now have a working JavaScript clock. Congratulations cooll;

Full code

HTML
Code: Select all
<!DOCTYPE HTML>
<html lang="en">
    <head>
        <title>JavaScript Clock</title>
        <link rel="stylesheet" type="text/css" href="style.css">
        <link href='http://fonts.googleapis.com/css?family=Roboto:400,700' rel='stylesheet' type='text/css'>
        <script type="text/javascript" src="engine.js"></script>
    </head>
    <body>
        <div id="title">
            <h2>JavaScript clock</h2>
        </div>
        <div id="wrapper">
            <canvas id="clockface" height="500" width="500"></canvas>
        </div>
        <div id="copyright">
            <p>Design and code by <a href="http://www.comathi.com">comathi</a></p>
            <p>Go crazy!</p>
        </div>
    </body>
</html>
CSS
Code: Select all
/*Reset*/
* {
    margin: 0;
    padding: 0;
    font-family: 'Roboto', sans-serif;
    color: #fff;
}

body
{
    background: url('bg.png');
}

#title
{
    margin-top: 25px;
}

#title h2
{
    text-align: center;
}

#wrapper
{
    width: 500px;
    
    border: 10px solid rgba(33,96,182,0.7);
   
    -webkit-border-radius: 50%;
    -moz-border-radius: 50%;
    border-radius: 50%;
    
    margin: 50px auto 0 auto;
}

#clockface
{
    height: 500px;
    width: 500px;
}
JavaScript
Code: Select all
var hourHand = new Object();
    hourHand.x1 = 250;
    hourHand.y1 = 250;
    hourHand.x2 = 250;
    hourHand.y2 = 150;
    hourHand.length = 100;

var minuteHand = new Object();
    minuteHand.x1 = 250;
    minuteHand.y1 = 250;
    minuteHand.x2 = 250;
    minuteHand.y2 = 100;
    minuteHand.length = 150;

var secondHand = new Object();
    secondHand.x1 = 250;
    secondHand.y1 = 250;
    secondHand.x2 = 250;
    secondHand.y2 = 50;
    secondHand.length = 200;

//Canvas and context
var c, cont;

//Angles
var hA, mA, sA;

function drawclock()
{
    //Clear the clock
    cont.clearRect(0, 0, c.width, c.height);
        
    //Draw the hour hand
    cont.strokeStyle = "#FF0000";
    cont.beginPath();
    cont.moveTo(hourHand.x1, hourHand.y1);
    cont.lineTo(hourHand.x2, hourHand.y2);
    cont.lineWidth = 15;
    cont.stroke();
    
    //Draw minute hand
    cont.strokeStyle = "#FFFFFF";
    cont.beginPath();
    cont.moveTo(minuteHand.x1, minuteHand.y1);
    cont.lineTo(minuteHand.x2, minuteHand.y2);
    cont.lineWidth = 5;
    cont.stroke();
    
    //Draw the second hand
    cont.strokeStyle = "#FFFFFF";
    cont.beginPath();
    cont.moveTo(secondHand.x1, secondHand.y1);
    cont.lineTo(secondHand.x2, secondHand.y2);
    cont.lineWidth = 3;
    cont.stroke();
    
    //Draw the center cirle
    cont.fillStyle = "#00FF00";
    cont.beginPath();
    cont.arc(250, 250, 15, 0, Math.PI * 2, true);
    cont.fill();
    
    //Draw the ticks
    for (var i=1; i<13; i++)
    {
        var x1 = 250;
        var x2 = 250;
        var y1 = 25;
        var y2 = 0;
        
        cont.strokeStyle = "#FFFFFF";
        cont.beginPath();
        cont.moveTo(250 + 225 * Math.cos((2 * Math.PI) * (i / 12)), 250 + 225 * Math.sin((2 * Math.PI) * (i / 12)));
        cont.lineTo(250 + 250 * Math.cos((2 * Math.PI) * (i / 12)), 250 + 250 * Math.sin((2 * Math.PI) * (i / 12)));
        cont.lineWidth = 3;
        cont.stroke();
    }

}

function clockwork()
{
    var time = new Date ( );
  
    //Get the current hour, minute and second
    var h = time.getHours();
    var m = time.getMinutes();
    var s = time.getSeconds();
    
    hA = (2 * Math.PI) * (h / 12) - (2 * Math.PI / 4);
    
    mA = (2 * Math.PI) * (m / 60) - (2 * Math.PI / 4);
    
    sA = (2 * Math.PI) * (s / 60) - (2 * Math.PI / 4);
    
    //Change the hour hand's location
    hourHand.x2 = hourHand.x1 + hourHand.length * Math.cos(hA);
    hourHand.y2 = hourHand.y1 + hourHand.length * Math.sin(hA);
    
    //Change the minute hand's location
    minuteHand.x2 = minuteHand.x1 + minuteHand.length * Math.cos(mA);
    minuteHand.y2 = minuteHand.y1 + minuteHand.length * Math.sin(mA);
    
    //Change the second hand's location
    secondHand.x2 = secondHand.x1 + secondHand.length * Math.cos(sA);
    secondHand.y2 = secondHand.y1 + secondHand.length * Math.sin(sA);
    
    drawclock();
}

function init()
{
    c = document.getElementById("clockface");
    cont = c.getContext("2d");
    
    clockwork();
    
    setInterval(function(){clockwork();},1000);
}

onload = init;
1 post Page 1 of 1
Return to “Tutorials”