Analog Clock with JavaScript
Today, we will again use the Power of Scalable Vector Graphics (SVG) to build something awesome: An analog clock. By analog, I mean Watch with hands, if that makes sense.
The clock will look like this.
¶HTML for the Analog Clock
Let’s start by reviewing the Markup for our little Website. We mostly have the HTML Boilerplate; we just add a script and link tag to your JavaScript and CSS files. We also create an SVG with a view box of -50 -50 100 100
, which means elements placed at 0 0 will be in the middle, like the two circles we add within our SVG.
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Clock</title>
<script defer src="clock.js"></script>
<link rel="stylesheet" href="style.css">
</head>
<body>
<svg viewBox="-50 -50 100 100">
<circle cx="0" cy="0" r="49.5" class="clock" id="clock"></circle>
<circle cx="0" cy="0" r="2" class="clock-joint" id="clock"></circle>
</svg>
</body>
</html>
¶CSS for the Analog Clock
Next, let’s go over the CSS. We start by simply setting box-sizing
to border-box
for every element; this makes working with padding and margin much easier.
* {
box-sizing: border-box;
}
Then we make it so the Body has no margin, a little padding, and it is as high as the Viewport.
body {
height: 100vh;
margin: 0;
padding: 2rem;
background-color: hsl(0, 0%, 13%);
}
After that, we make it so the SVG, which will be the clock, can’t be wider or higher than the screen, but we want it to grow as much as possible. To also center it, we can add margin: auto
.
.clock {
box-shadow: 0 0 5px hsl(0, 0%, 0%);
stroke: hsl(0, 0%, 30%);
fill: hsl(0, 0%, 20%);
}
Lastly, we set the Fill and stroke colors for the circles. Stroke is like the border, and fill is the background color.
.clock {
stroke: hsl(0, 0%, 30%);
fill: hsl(0, 0%, 20%);
}
.clock-joint {
fill: hsl(0, 0%, 90%);
}
¶JavaScript for the Analog Clock
In the JavaScript file, we start by getting references for several DOM Nodes that we will need later.
const clockNode = document.getElementById('clock')
const svgNode = document.querySelector('svg')
const handsContainer = document.getElementById('hands')
Continuing, we create the lines that run along the edge of the clock: the seconds, minutes, and hours. To do this, we use the addHand
function that we go over later.
const numTimes = 60
for (i = 0; i < numTimes; i++) {
const handRotationOffset = 360 / numTimes
if ((i % 15 == 0 || i == 0) && numTimes != i + 1) {
console.log('15er');
addHand({ rotation: i * handRotationOffset, color: 'hsl(0, 0%, 80%)', width: 15 })
} else if (i % 5 == 0 && numTimes != i + 1) {
console.log('5er');
addHand({ rotation: i * handRotationOffset, color: 'hsl(0, 0%, 40%)', width: 12 })
}
else {
addHand({ rotation: i * handRotationOffset, color: 'hsl(0, 0%, 30%)' })
}
}
After that, we create the actual hands that indicate the seconds, minutes, and hours. We save these for later.
const secondHand = addHand({ color: 'hsl(0, 0%, 90%)', width: 40, x: 0, height: 1 })
const minuteHand = addHand({ color: 'hsl(0, 0%, 90%)', width: 25, x: 0, height: 1.5 })
const hourHand = addHand({ color: 'hsl(0, 0%, 90%)', width: 15, x: 0, height: 2 })
Lastly, we create a function called render, which will check the time and adjust the hands appropriately. We use the requestAnimationFrame
function for rerendering the clock automatically but also for Performance.
render()
function render() {
const time = new Date()
const secondsDegrees = (time.getSeconds() * 6) + 90;
const minutesDegrees = (time.getMinutes() * 6) - 90;
const hoursDegrees = (time.getHours() * 30) - 90;
secondHand.style.transform = `rotate(${secondsDegrees}deg)`
minuteHand.style.transform = `rotate(${minutesDegrees}deg)`
hourHand.style.transform = `rotate(${hoursDegrees}deg)`
requestAnimationFrame(render)
}
¶addHand
Function
Below, you also see the Implementation of the Add Hand. As you see, we use the g
as a pivot to rotate the rect to the correct Position.
function addHand({ width = 10, height = 1.5, x = 35, rotation = 0, color = 'black' } = {}) {
const handNode = document.createElementNS('http://www.w3.org/2000/svg', 'rect')
const handNodeGroup = document.createElementNS('http://www.w3.org/2000/svg', 'g')
handNode.setAttribute('width', width)
handNode.setAttribute('height', height)
handNode.setAttribute('x', x)
handNode.setAttribute('fill', color)
handNodeGroup.style.transform = `rotate(${rotation - 90}deg)`
handNodeGroup.style.transformOrigin = `0px ${height / 2}px`
handNodeGroup.style.translate = `0px -${height / 2}px`
handNodeGroup.appendChild(handNode)
svgNode.appendChild(handNodeGroup)
return handNodeGroup
}
¶Conclusion
So that’s it. I hope you had a good time and that you learned something!
Visit the Demo.
Leave a Reply