JavaScript Clocks: Animating SVG

In our last article, we looked at the very basics of SVG: just enough to draw a hand on a blank clock face. In this article, we’ll be looking at how to animate those elements using JavaScript.

Let’s take a look at where we left off last time.

<html>
  <head>
    <title>
      Clock
    </title>
  </head>
  <body>
    <svg>
      <rect x="50%" y="5%" width="1%" height="45%" 
            transform="rotate(45, 50, 50)" />
    </svg>
  </body>
</html>

Our next goal is to make the second hand move every second. In the tutorial for making a digital clocks we learned about the Date object and its method getSeconds(). We also learned about the setInterval() method and how it executes the same function over and over with a set interval between executions.

First, let’s do a quick review of the Date object and how to use its methods.

let time = new Date();
let seconds = time.getSeconds();

The variable seconds will be an integer value equal to the number of seconds past the current minute (0–59) whenever that code ran. It will not update as time passes however. For that we need setInterval().

let clockInterval = setInterval(function() {
  let time = new Date();
  let seconds = time.getSeconds();
}, 100);

Awesome! Now our variable seconds will be updated every 100 ms. We just need a way to convert our seconds into degrees so we can plug it into our transform attribute. Here’s where our old friend 𝕄𝕒𝕥𝕙© comes in. Circles are made up of 360 degrees. We need to portion those out into chunks equal to the number of seconds in a minute.

360 ÷ 60 = 6

Every second we need to advance our hand by 6°. If one second has passed, our hand should be rotated by 6° (1 × 6 = 6); if five seconds have passed our hand should be rotated by 30° (5 × 6 = 30).

let clockInterval = setInterval(function() {
  let time = new Date();
  let seconds = time.getSeconds();
  let secAngle = seconds * 6;
}, 100);

Finally, we need a way to modify the transform attribute of our <rect> element. For that, we have the setAttribute() method of the Element API. To use it, we’re going to give our <rect> an ID and use the getElementById() method from the Document API that we talked about in the first article in this series.

<rect id="rect" x="50%" y="5%" width="1%" height="45%" />

The setAttribute() method takes two parameters: the name of the attribute we want to set or change, and the value we want to set it to. In our case we want to change the transform attribute, and the value we want to change it to is going to be the rotation function.

let rect = document.getElementById("rect");
let clockInterval = setInterval(function() {
  let time = new Date();
  let seconds = time.getSeconds();
  let secAngle = seconds * 6;
  rect.setAttribute("transform", `rotate(${secAngle}, 50, 50)`);
}, 100);

Cool. Now let’s add the minute and hour hands! The process is going to be largely the same. First we need to add two more <rect> elements to our HTML and give all of them unique IDs. We also need to make sure that each hand is different so we can tell them apart.

<rect id="seconds" x="50%" y="5%" width="1%" height="45%" />
<rect id="minutes" x="49%" y="15%" width="2%" height="35%" />
<rect id="hours" x="48%" y="25%" width="4%" height="25%" />

Next we have to put in some code to handle how they move. The minute hand, like the second hand, will have 60 positions around the clock face so we can use the same formula to determine its angle. The hour hand however, only has 12 positions around the clock face so we’ll need to modify our formula to accommodate that (360 ÷ 12 = 30).

let sec = document.getElementById("seconds");
let min = document.getElementById("minutes");
let hrs = document.getElementById("hours");
let clockInterval = setInterval(function() {
  let time = new Date();
  let seconds = time.getSeconds();
  let secAngle = seconds * 6;
  sec.setAttribute("transform", `rotate(${secAngle}, 50, 50)`);
  let minutes = time.getMinutes();
  let minAngle = minutes * 6;
  min.setAttribute("transform", `rotate(${minAngle}, 50, 50)`);
  let hours = time.getHours();
  let hrsAngle = hours * 30;
  hrs.setAttribute("transform", `rotate(${hrsAngle}, 50, 50)`);
}, 100);

Ladies and gentlemen, we have an analog clock! Granted, it’s not the prettiest clock on the Internet, but it’s ours. There’s a lot we could do to make it prettier and more efficient but this is a great start!

If you make a clock based off of this tutorial I’d love to see it. Send me a link in the comments!

Reference

<svg>
<rect>
transform
Date
Date.prototype.getSeconds()
Date.prototype.getMinutes()
Date.prototype.getHours()
Document.getElementById()
Element.setAttribute()
WindowOrWorkerGlobalScobe.setInterval()
Template Literals