Skip to content

Clock Numbers

Now that we have a clock face, let’s add the numbers (1 through 12). We’ll create a JavaScript function drawNumbers that calculates the position of each number around the clock and renders them.

The drawClock function will be updated to call both drawFace (from Part I) and our new drawNumbers function.

// Assuming drawFace(ctx, radius) is defined from a previous part.
function drawNumbers(context, clockRadius) {
context.fillStyle = '#333'; // Set text color
context.font = `${clockRadius * 0.15}px Arial, sans-serif`; // Font size relative to radius
context.textBaseline = "middle"; // Align text vertically to the center
context.textAlign = "center"; // Align text horizontally to the center
for (let num = 1; num <= 12; num++) {
// Calculate the angle for each number
// Each number is 30 degrees (PI/6 radians) apart
// num * Math.PI / 6 positions numbers clockwise starting from 3 o'clock if not adjusted.
// To place '12' at the top (0 radians effectively), '1' at PI/6, etc.,
// we can calculate angle as (num * Math.PI / 6).
// The actual drawing will be at (0, -distance) in a rotated context.
const angle = num * Math.PI / 6;
// Save the current context state (optional, but good for complex transforms)
// context.save();
// Rotate to the angle where the number should be placed
context.rotate(angle);
// Translate out along the new y-axis (which points towards the number's position)
context.translate(0, -clockRadius * 0.85); // Position numbers at 85% of radius
// Rotate back to make the number draw upright, not tilted
context.rotate(-angle);
context.fillText(num.toString(), 0, 0);
// Restore transformations to draw the next number correctly
// (Undo the rotations and translation for this specific number)
context.rotate(angle); // Rotate back to number's angle position
context.translate(0, clockRadius * 0.85); // Translate back to the clock center
context.rotate(-angle); // Rotate canvas back to its original alignment (0 degree pointing to 3 o'clock)
// Alternative using save/restore for each number:
// context.restore();
}
}
// Updated main drawing function
function drawClock(context, clockRadius) {
drawFace(context, clockRadius);
drawNumbers(context, clockRadius);
}

// Full script for this part (includes drawFace for completeness) const canvas = document.getElementById(“canvas”); const ctx = canvas.getContext(“2d”); let radius = canvas.height / 2; ctx.translate(radius, radius); radius *= 0.90;

function drawFace(context, r) { context.beginPath(); context.arc(0, 0, r, 0, 2 * Math.PI); context.fillStyle = ‘white’; context.fill(); context.strokeStyle = ‘#333’; context.lineWidth = r * 0.1; context.stroke(); context.beginPath(); context.arc(0, 0, r * 0.05, 0, 2 * Math.PI); context.fillStyle = ‘#333’; context.fill(); }

function drawNumbers(context, clockRadius) { context.fillStyle = ‘#333’; context.font = ${clockRadius * 0.15}px Arial, sans-serif; context.textBaseline = “middle”; context.textAlign = “center”;

for (let num = 1; num <= 12; num++) {
const angle = num * Math.PI / 6;
context.rotate(angle);
context.translate(0, -clockRadius * 0.85);
context.rotate(-angle);
context.fillText(num.toString(), 0, 0);
context.rotate(angle);
context.translate(0, clockRadius * 0.85);
context.rotate(-angle);
}

}

function drawClock(context, r) { drawFace(context, r); drawNumbers(context, r); }

drawClock(ctx, radius);

  1. Font and Text Alignment:
    • context.fillStyle = '#333';: Sets the color of the numbers.
    • context.font = ${clockRadius * 0.15}px Arial, sans-serif;: Sets the font size dynamically based on 15% of the clock’s radius. Using a template literal for string construction is a modern JS feature. ‘Arial, sans-serif’ provides a common font with a generic fallback.
    • context.textBaseline = "middle"; and context.textAlign = "center";: These are crucial for precise text placement. They ensure that the coordinates (0,0) used in fillText refer to the horizontal and vertical center of the text.
context.font = `${clockRadius * 0.15}px Arial, sans-serif`;
context.textBaseline = "middle";
context.textAlign = "center";
  1. Looping Through Numbers (1 to 12):

    • A for loop iterates from num = 1 to 12.
  2. Calculating Number Positions (Angle, Rotate, Translate):

    • const angle = num * Math.PI / 6;: Calculates the angle for each number. The 12 numbers are spaced 30 degrees (Math.PI / 6 radians) apart. num = 1 (1 o’clock) will be at 30 degrees, num = 2 (2 o’clock) at 60 degrees, and so on, relative to the 3 o’clock direction (which is 0 radians in standard Cartesian coordinates, but our drawing method effectively makes 0 radians map to 12 o’clock on the clock face due to how lineTo(0, -length) is used after rotation).
    • context.rotate(angle);: Rotates the canvas coordinate system to the angle of the current number.
    • context.translate(0, -clockRadius * 0.85);: Moves the origin along the new Y-axis (which is now pointing towards the number’s position) by 85% of the radius. This is where the number will be drawn.
    • context.rotate(-angle);: This is important! After rotating the canvas to position the drawing spot, we rotate it back by the negative of that angle. This ensures the number itself is drawn upright, not tilted along with the main rotation.
    • context.fillText(num.toString(), 0, 0);: Draws the number at the new (0,0) (which is the translated and re-rotated spot).
    • Restoring Transformations: After drawing a number, the transformations applied for that specific number must be undone to correctly position the next number. This involves:
      • context.rotate(angle); (Re-apply rotation to get back to number’s placement orientation)
      • context.translate(0, clockRadius * 0.85); (Translate back to clock center along that orientation)
      • context.rotate(-angle); (Rotate system back to its original state before this iteration)
    • This explicit undoing of transformations can also be managed more simply by using context.save() before the transformations for a number and context.restore() after drawing it. This isolates the transformations for each number.
for (let num = 1; num <= 12; num++) {
const angle = num * Math.PI / 6;
context.rotate(angle); // Point to where number should be
context.translate(0, -clockRadius * 0.85); // Move to that spot
context.rotate(-angle); // Straighten canvas for text drawing
context.fillText(num.toString(), 0, 0);
// Undo transformations for next iteration
context.rotate(angle); // Rotate back to number's angle
context.translate(0, clockRadius * 0.85); // Move back to center
context.rotate(-angle); // Reset canvas rotation to original state
}

With these steps, the numbers are drawn neatly around the clock face. The next part will cover drawing the clock hands.