Compared to the JavaScript digital clock I demonstrated in the previous article, this SVG analog clock uses fewer lines of code, and has the advantage of being infinitely scalable.
I’ve always admired the elegance of the code, expressed earlier by Felix Gnass. First, there’s my version of the SVG, which consists of just four elements: the dial, and three rectangular hands (hours, minutes, and seconds):
<svg id="clock" viewBox="0 0 100 100">
<circle id="face" cx="50" cy="50" r="45"/>
<g id="hands">
<rect id="hour" x="48.5" y="12.5" width="5" height="40" rx="2.5" ry="2.55" />
<rect id="min" x="48" y="12.5" width="3" height="40" rx="2" ry="2"/>
<line id="sec" x1="50" y1="50" x2="50" y2="16" />
</g>
</svg>
The markup is, I hope, fairly self-explanatory: the <circle>
element has a radius of 45 units, with its center 50 units from the top left corner of the SVG element; the hour
, min
and sec
elements are positioned similarly. The hour and minute hands are rectangles 40 units long, and have a border-radius
(in SVG, applied as rx
and ry
).
Rather than trying to style the elements inline, I’ll do that in CSS:
#face {
stroke-width: 2px; stroke: #fff;
}
#hour, #min, #sec {
stroke-width: 1px; fill: #333; stroke: #555;
}
#sec { stroke: #f55; }
At this point, the clock has all hands pointing to 12. The JavaScript, written at the bottom of the page, changes all that:
setInterval(function() {
function r(el, deg) {
el.setAttribute('transform', 'rotate('+ deg +' 50 50)')
}
var d = new Date()
r(sec, 6*d.getSeconds())
r(min, 6*d.getMinutes())
r(hour, 30*(d.getHours()%12) + d.getMinutes()/2)
}, 1000)
This differs from the previous script in several significant ways:
First, it uses a setInterval
that encompasses the entire script. The function inside runs every second, due to the 1000
passed to setInterval
as an argument in milliseconds.
d
is a variable based on the Date()
object, just like the previous example. For brevity, the script takes advantage of the fact that elements with an id
attribute automatically become references in scripts. While this isn’t a recommended practice, it’s certainly doable, making the use of sec
, min
and hour
in the script direct references to the matching SVG elements.
The r
function takes two arguments: the element to change, and the amount of rotation. Note that we’re taking about SVG rotation, not CSS, so there’s no need for vendor prefixes.
For sec
, r
is passed the id
of the element, and 6 × the number of seconds in the current time. If it’s 0 seconds, deg
will be 0
, meaning that the element won’t rotate at all. If it’s 30 seconds, 30 × 6 = 180° of rotation. (The 50 50
value ensures that the element always rotates from the center of the SVG). min
takes gets the same treatment, while hour
is a little tricker: it uses the remainder of dividing the current hour by 12, multiplied by 30, and adds the number of minutes divided by 2. In other words, if it is 15 minutes past midnight, the calculation becomes:
r(hour, 30 * 0 + (15 / 2))
> 7.5
It if it is 3.45pm (remembering that JavaScript uses 24 hour time by default, so the hour division will be 15 % 12
. The calculation is:
r(hour, 30 * 3 + (45 / 2))
> 112.5
All told, it’s a very nice little piece of code, which I hope will find its uses in your projects.
Enjoy this piece? I invite you to follow me at twitter.com/dudleystorey to learn more.
Check out the CodePen demo for this article at https://codepen.io/dudleystorey/pen/HLBki