DEV Community

Cover image for Minimalist Wall Clock
Anik Khan
Anik Khan

Posted on

Minimalist Wall Clock

I have created this project as part of the JavaScript30 Challenge by Wes Bos. It was to create a simple analog clock. Even though it was simple but it got me real hard especially with the CSS part and second to degree conversion (more on that later).

A bit about myself

I am a backend developer, recently trying to enhance my knowledge in frontend. It's fairly complicated for me to think in CSS & DOM way. Thus doing this sort of challenge keeps me pushing my boundary. This is the best way of learning, even though it's pretty frustrating like this very one😁

Live URL:

http://minimal-clock.surge.sh/

Alt Text

HTML Part

The HTML part seemed so simple at first. But it gave me a very important lesson- Try to think in parts.
Here's what I meant-

  • Try to imagine a clock and its parts before diving into HTML
  • A clock has a body, a clock face. And under the clock face it has 3 hands- the second hand, the minute hand, the hour hand.

Now put each of these parts as HTML elements and assign them classes based on their behavior.


<body>

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


    <!-- This audio part is extra; just a treat for me😉 -->
    <!-- Song name Snowplay by WYS -->
    <audio controls autoplay>
        <source src="./audio/Snowman.mp3" type="audio/mpeg">
    </audio>

    <script src="script.js"></script>
</body>
Enter fullscreen mode Exit fullscreen mode

CSS Part

CSS is like a catfish; One moment I thought aha! gotcha, next moment I thought I was just hallucinating.
I am still learning about the flexbox so while coding by myself I had to go through a lot of searches just to center the clock & understanding the purpose of each property.
Anyway, let's jump into the code-

The main job here to put the image and set it up the correct way so that it's neither stretched out too much nor repeat itself like tiles.

html {
    /* The image is from unsplash by David van Dijk */
    /* set an image such that it doesn't repeat stay center */
    background: url(./img/wall.jpg) no-repeat center center fixed;
    /* Resize the background image to cover the entire container */
    background-size: cover;

    /*setting the root font-size to calculate rem unit based off of this*/
    font-size: 10px;
    text-align: center;
}
Enter fullscreen mode Exit fullscreen mode

This part was pretty time consuming for me. I wanted to center the clock using flex even though I wasn't comfortable with it. But I learned why each property exists and their functionality-

body {
    margin: 0;

    /*I want elements of body to be displayed as flexbox*/
    display: flex;
    flex: 1;

    /*center vertically*/
    align-items: center;

    /*center horizontally*/
    justify-content: center;
    margin-left: -200px;

    /*100% of the viewport height*/
    min-height: 100vh;


    font-size: 2rem;
}
Enter fullscreen mode Exit fullscreen mode

This is where we target the actual clock container. And give it shape by setting its height and width. Fairly easy.
But box-shadow property is something amazing, I must say. It can take from 2 to 6 values. And you can define more than one shadow effect with just one property. Don't try to create two different properties for each effect cause it will overwrite the previous one.

.clock {
    /*give it a shape; from square to round*/
    height: 30rem;
    width: 30rem;
    border: 10px solid rgba(209, 231, 224, 0.904);
    border-radius: 50%;

    /*keep the hands away from border*/
    padding: 4px;

    /*set the shadow inside*/
    box-shadow: inset 0 0 10px black,
        inset 0 0 10px rgb(235, 233, 233);

    position: relative;
}
Enter fullscreen mode Exit fullscreen mode

The other parts are fairly straightforward and I put relevant comments

.clock-face {
    height: 50%;
    width: 50%;
    transform: translateY(-3px);

    position: relative;
}

.hand {
    /*give the hands some shape*/
    background-color: rgba(15, 80, 69, 0.712);
    height: 3px;
    width: 75%;

    /*relating to the position*/
    position: absolute;
    top: 150px;
    left: 30px;

    /*to move*/
    transform: rotate(90deg);
    /*keep everything attached to the right point or middle point*/
    transform-origin: 100%;
    /*transition & speeding the effect*/
    transition: all 0.06s;
    transition-timing-function: cubic-bezier(0.07, 3.65, 0.76, 0.4);
}

audio {
    /*positioning the audio player*/
    align-self: flex-end;
    margin-left: -300px;
}

/*after 444 degree the second hand behaves weirdly; to stop that- (more on that in script.js file)*/
.notransition {
    -webkit-transition: none !important;
    -moz-transition: none !important;
    -o-transition: none !important;
    transition: none !important;
}
Enter fullscreen mode Exit fullscreen mode

Be careful though! Here the unit for height and width are different. % is relative to the parent element. px is fixed
Important questions to be answered here-

  • How do we move the hands?

    using transform: rotate(90deg)

  • how do we move the hands but the common point for each hand remained the rightmost head?

    using transform-origin: 100%; By default it is 50% so the common point for the hands is 50%; we need to set it 100%.

Alt Text
When transform-origin: 50%

JavaScript Part

The js part was fine for me.

  • Get the DOM elements for second-hand
//get the DOM elements for hands
    const secondDiv = document.querySelector('.second-hand')
Enter fullscreen mode Exit fullscreen mode
  • Get the current time
//get the current time
    const now = new Date()
Enter fullscreen mode Exit fullscreen mode
  • Extract the second from that
//current second
    const second = now.getSeconds()
Enter fullscreen mode Exit fullscreen mode
  • Convert the second to degree
//convert second to degree
    const secondRatio = second / 60
    const secondDegree = (secondRatio * 360) + 90
Enter fullscreen mode Exit fullscreen mode
  • Add the transform CSS style to rotate based off of the calculated degree
 //add the transform css style rotate
    secondDiv.style.transform = `rotate(${secondDegree}deg)`
Enter fullscreen mode Exit fullscreen mode
  • Repeat the same for minutes and hours

Note about the second to degree conversion-

  • first imagine the clock. and imagine time: 10 min 20 s
  • For 20 s we want the second-hand at a position about 30% far from the start point
  • This 30% or 40% of the total second can be determined from ratio. In ratio, 0 means 0% or start, and 1 means 100% or finish.
  • To find ratio, we divide by the current value / highest value possible. so secondRatio = currentSecond / 60
  • Now we need to convert that to a degree for positioning. The degree is smaller than the seconds. Because at 15 seconds the degree is 90, at 30 sec the degree is 180. So to convert seconds to the degree we multiply. Thus, degree = secondRatio * 360
  • To understand why we added the extra 90, try remembering this- Alt Text Initially, we started off by adding 90deg. So for each calculated value, we need to keep adding that 90deg to maintain that.

You can find the full javascript details in the GitHub.

About the Design

Simplicity is the ultimate sophistication. You can see that I didn't use extensive use of color; the clock color suits with the wall as if they are together.
The different objects like the table lamp and the audio player is a different color just to distinguish themselves from the main part of the app.

GitHub

https://github.com/AKdeBerg/analog-clock


Images are from unsplash

Top comments (0)