Graphics Building the Clock Face
This lesson builds the static clock face-the circular dial and hour tick marks-using arc() and simple trigonometry as the foundation the next two lessons will animate.
Planning the Clock Face
An analog clock is really just three drawings layered on top of each other: a round face, a set of tick marks around the rim, and a pair (or trio) of rotating hands. Building it in canvas is a good way to combine arc(), path drawing, and trigonometry into one recognizable project. This lesson covers the face and ticks; the next lesson adds the hands, and the one after that makes it tick every second.
- A canvas element sized to fit the clock comfortably
- An outer circle for the dial, drawn with arc()
- A small filled circle at the center where the hands will pivot
- Twelve evenly spaced tick marks, one for each hour
- A coordinate system centered on the clock so angle math stays simple
Example
<!DOCTYPE html>
<html>
<head>
</head>
<body>
<canvas id="clock" width="300" height="300"></canvas>
<script>
const canvas = document.getElementById('clock');
const ctx = canvas.getContext('2d');
const radius = canvas.height / 2;
ctx.translate(radius, radius);
const clockRadius = radius * 0.9;
function drawFace(ctx, radius) {
ctx.beginPath();
ctx.arc(0, 0, radius, 0, 2 * Math.PI);
ctx.fillStyle = '#f5f5f5';
ctx.fill();
ctx.lineWidth = radius * 0.04;
ctx.strokeStyle = '#333333';
ctx.stroke();
ctx.beginPath();
ctx.arc(0, 0, radius * 0.05, 0, 2 * Math.PI);
ctx.fillStyle = '#333333';
ctx.fill();
}
drawFace(ctx, clockRadius);
</script>
</body>
</html>Notice the ctx.translate(radius, radius) call before anything is drawn. It shifts canvas coordinate (0, 0) to the visual center of the clock, so every shape from here on can be drawn as if the center of the dial were the origin. Without that translate, every arc, tick, and hand would need its own centerX/centerY offset added by hand.
Placing the Hour Ticks with Trigonometry
A full circle is 2*PI radians, and a clock face has 12 hour positions, so each hour is 2*PI / 12, or PI / 6 radians, apart. Looping from 0 to 11 and multiplying the loop index by PI / 6 gives the angle for every tick. Rather than converting that angle into x and y coordinates by hand, it is simpler to rotate the whole canvas context to that angle, draw a short vertical line pointing 'up' (negative y, since canvas y grows downward), then undo the rotation before moving to the next tick.
Example
<!DOCTYPE html>
<html>
<head>
</head>
<body>
<canvas id="clock" width="300" height="300"></canvas>
<script>
const canvas = document.getElementById('clock');
const ctx = canvas.getContext('2d');
const radius = canvas.height / 2;
ctx.translate(radius, radius);
const clockRadius = radius * 0.9;
function drawTicks(ctx, radius) {
for (let i = 0; i < 12; i++) {
const angle = i * (Math.PI / 6); // 360 degrees / 12 hours
ctx.save();
ctx.rotate(angle);
ctx.beginPath();
ctx.lineWidth = radius * 0.03;
ctx.moveTo(0, -radius * 0.9);
ctx.lineTo(0, -radius * 0.85);
ctx.stroke();
ctx.restore();
}
}
drawTicks(ctx, clockRadius);
</script>
</body>
</html>