CSS and JS clock

This is the second project of WesBos's JS30 series. To see the whole 30 part series, click here We will be building a "clock" as using divs, some CSS animation and javascript. The idea is to replicate a minimalistic analog wall clock with an hours, minutes and seconds hand. Here is the video below -

Here is the codepen for the starter file

The HTML is simple and minimalistic

<div class="clock">
  <div class="clock-face">
    <div class="hand hour-hand"></div>
    <div class="hand min-hand"></div>
    <div class="hand second-hand"></div>
  </div>
</div>

The div.clock is the circular outer body of the clock, the div.clock-face is the container for the hands. The three children divs represent the hour, minute and seconds hand respectively (as is evident by the class names).

The CSS is slightly more interesting (and involved). The clock outline is just a div with the border radius and border width set. Some box shadow for the styling. I learnt that you can apply mutiple box shadows to a single element, something I did not know before!

General box shadow format : box-shadow: offset-x | offset-y | blur-radius | spread-radius | color. You can have multiple box shadows by comma seperating the values as seen below. You can learn more about the box-shadow property @ MDN Docs

.clock {
    border:20px solid white;
    border-radius:50%;
    box-shadow:
    0 0 0 4px rgba(0,0,0,0.1),
    inset 0 0 0 3px #EFEFEF,
    inset 0 0 10px black,
    0 0 10px rgba(0,0,0,0.2);
}

div.clock-face is the container which holds the three hands. The height of each hand is 6px, so to account for it the container is moved up by 3px.

.clock-face {
    transform: translateY(-3px);
}
.hand {
    /* 50% width of div.clock-face */
    width:50%;
    height:6px;

    /* places it in between (the clock-face's translateY(-3px) takes care of the offset) */
    position: absolute;
    top:50%;

    transform-origin: 100%;
    transform: rotate(90deg);
    transition: all 0.05s;
    transition-timing-function: cubic-bezier(0.1, 2.7, 0.58, 1);
}

The transform-origin gives the point at which the rotation transform is to take place - default would be at the center of the hand, but we want it to rotate around the right endpoint. Setting transform-origin: 100% does this.

The hands by default are at 0deg which is horizontal, we set the transform to rotate(90deg) to set them all in a vertial position - 12:00:00. The transition and transition-timing-function give the animated effect when chaging the angle of the hands.

Now the javascript bits! We can break it down into the following steps -

  1. Get the current time
  2. Extract the hours, minutes and seconds
  3. Map the hours, minutes and seconds to corresponding degrees of the hands
  4. Update the hands' transform

Now the whole loop should run every second, let's abstract the update logic in the setDate() function

//get reference to the hands
const secondHand = document.querySelector('.second-hand')
const minuteHand = document.querySelector('.min-hand')
const hourHand = document.querySelector('.hour-hand')

function setDate(){
  //logic to update the clock
}

setInterval(setDate, 1000)

Now all the logic that goes into the setDate()

Get the current time

const now = new Date()

Extract the current sec, min, hour

const seconds = now.getSeconds()
const mins = now.getMinutes()
const hours = now.getHours()

Map the time to degrees of the corresponding hands

const secondsDegrees = ((seconds / 60) * 360) + 90;
const minsDegrees = ((mins / 60) * 360) + ((seconds/60)*6) + 90;
const hoursDegrees = ((hours / 12) * 360) + ((mins/60)*30) + 90;

Here the 90 represents 90 degrees that is to be added to set the hands to 12 O'clock (which by default they point to 9 O'clock). Rest is simple mapping from one range to another.

Update the hand positions

secondHand.style.transform = `rotate(${secondsDegrees}deg)`
minuteHand.style.transform = `rotate(${minsDegrees}deg)`
hourHand.style.transform = `rotate(${hoursDegrees}deg)`

That is pretty much it! Put it all together and you get the finished version of the clock. The codepen with the final code below -

Note I've added the following style to make the min and hour hands seprate lengths,

.min-hand {
  width: 35%;
  margin-left: 15%;
}

.hour-hand {
  width: 20%;
  margin-left: 30%;
}