I’ve previously demonstrated an analog clock made with SVG and JavaScript, which I thought used a particularly elegant technique. While it’s possible to create the clock movement with HTML elements, CSS transforms and animation, it’s not possible to set such a clock to the correct time for the user. To do that, we need JavaScript… and if we’re starting with that, it made sense to experiment with the Web Animation API, showing that it too can use steps()
animation for the second hand. (Due to the newness of the Web Animation API, this demo will currently only work in Chrome and Firefox).
Markup & CSS
The markup consists of a <div>
with three <span>
elements inside it:
<div id="clock">
<span id="minutehand"></span>
<span id="hourhand"></span>
<span id="secondhand"></span>
</div>
The attached styles:
#clock {
width: 30vw; height: 30vw;
border-radius: 50%;
border: 5px double #333;
background: radial-gradient(#333, #000);
margin: 2rem auto;
position: relative;
}
#hourhand, #minutehand, #secondhand {
position: absolute;
background: white;
transform-origin: bottom center;
box-shadow: 0 0 4px 4px rgba(0, 0, 0, 0.3);
}
The hands are absolutely positioned inside the relatively positioned clock, with their transforms centered at the bottom of each hand. The hands are exactly positioned using calc
, accounting for their different widths and heights:
#hourhand {
width: 4%;
height: 30%;
left: calc(50% - (4% / 2));
top: calc(50% - 30%);
}
#minutehand {
width: 2%;
height: 40%;
left: calc(50% - (2% / 2));
top: calc(50% - 40%);
}
#secondhand {
width: 1%;
height: 45%;
background: red;
left: calc(50% - (1% / 2));
top: calc(50% - 45%);
}
The Script
The JavaScript, added to the bottom of the page:
var d = new Date(),
hands = [secondhand,minutehand,hourhand],
initDeg = [6*d.getSeconds(), 6*d.getMinutes(), 30*(d.getHours()%12) + d.getMinutes()/2];
for (var i = 0; i < hands.length; i++) {
var stepper = i == 0 ? 60 : 0;
var animate = hands[i].animate([
{ transform: 'rotate(' + initDeg[i] + 'deg)' },
{ transform: 'rotate(' + (initDeg[i] + 360) + 'deg)' }
], {
duration: 1000 * Math.pow(60, i + 1),
easing: 'steps(' + stepper + ', start)',
iterations: Infinity
});
}
A quick explanation:
d
grabs the current datehands
is an array of the clock handid
sinitDeg
creates an array of the current time - hours, minutes and seconds - converted into degrees.- the
for
loop goes through the entire array, incrementing the variablei
- If
i
is0
, the variablestepper
is set to60
using a ternary operator. - the Web Animation API transforms each of the hands from their initial orientation (i.e. the current time) to 360° from this position; the duration is determined (in milliseconds) by multiplying 60 to the power of
i
+ 1. - if
stepper
is60
, that value is used forsteps()
; since only the second hand will have this quality, it “ticks” in the animation. The other clock hands receivesteps(0)
, effectively creating linear animation for them.
While it’s written in JavaScript, the animation still isn’t super precise, so please don’t depend on this clock for your personal schedule!
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/WwPzZX