Create a Totally Awesome Analog Clock

This is a companion discussion topic for the Creating an Awesome Analog Clock tutorial! :robot:

You can see a live version of the analog clock here: https://www.kirupa.com/html5/examples/awesome_analog_clock.htm

If you prefer a video, my tweet for this has it embedded:

Lastly, if you want the full code, take a look below:

<!DOCTYPE html>
<html>

<head>
  <meta charset="utf-8">
  <title>Awesome Analog Clock</title>

  <style>
    body {
      background-color: #EEE;
    }

    .section {
      background-color: #FFF;
      padding: 20px;
      width: 550px;
      border-radius: 5px;
      margin: 0 auto;
      margin-top: 30px;

      display: grid;
      justify-content: center;
    }

    #analogClock {
      display: grid;
      justify-items: center;
      align-items: center;
    }

    #analogClock > * {
      grid-area: 1 / 1;
    }

    #analogClock .center {
      width: 10px;
      height: 10px;
      background-color: #E83151;
      border: 5px solid #FFF;
      border-radius: 50%;
      position: relative;
      z-index: 100;
    }

    #analogClock .second,
    #analogClock .hour,
    #analogClock .minute {
      box-shadow: 0 0 10px #222;
      position: relative;
      transform-origin: left;
      border-radius: 10px;
    }

    #analogClock .second {
      height: 0px;
      width: 140px;
      border-top: 2px solid #E83151;

      left: 70px;
      top: 0px;

      transform: rotate(var(--seconds));
    }

    #analogClock .hour {
      height: 0px;
      width: 75px;
      border-top: 8px solid #FFF;

      left: 40px;
      top: 0px;

      transform: rotate(var(--hours));
    }

    #analogClock .minute {
      height: 0px;
      width: 140px;
      border-top: 4px solid #FFF;

      left: 70px;
      top: 0px;

      transform: rotate(var(--minutes));
    }
  </style>
</head>

<body>
  <div class="section">
    <div id="analogClock">
      <svg width="448" height="448" xmlns="http://www.w3.org/2000/svg">
        <g fill="none" fill-rule="evenodd">
          <path fill="#FFF" d="M-9-9h501v501H-9z" />
          <g transform="translate(24 24)">
            <circle stroke="#0041C5" stroke-width="24" fill="#0054FF" stroke-linecap="square" cx="200" cy="200" r="212" />
            <g fill="#FFF" font-family="HelveticaNeue-Bold, Helvetica Neue, Arial, sans-serif" font-size="100"
              font-weight="bold" opacity=".9">
              <text transform="translate(34.943 23.908)">
                <tspan x="107.159" y="98">12</tspan>
              </text><text transform="translate(34.943 23.908)">
                <tspan x="264.614" y="218.46">3</tspan>
              </text><text transform="translate(34.943 23.908)">
                <tspan x="3.464" y="218.46">9</tspan>
              </text><text transform="translate(34.943 23.908)">
                <tspan x="136.798" y="336.161">6</tspan>
              </text></g>
            <g transform="translate(13.793 13.22)">
              <circle fill-opacity=".5" fill="#FFF" cx="3.678" cy="186.667" r="3.678" />
              <circle fill-opacity=".5" fill="#FFF" cx="27.586" cy="96.552" r="3.678" />
              <circle fill-opacity=".5" fill="#FFF" cx="95.632" cy="27.586" r="3.678" />
              <circle fill-opacity=".5" fill="#FFF" cx="185.747" cy="3.678" r="3.678" />
              <circle fill-opacity=".5" fill="#FFF" cx="276.782" cy="27.586" r="3.678" />
              <circle fill-opacity=".5" fill="#FFF" cx="344.828" cy="96.552" r="3.678" />
              <circle fill-opacity=".5" fill="#FFF" cx="368.736" cy="186.667" r="3.678" />
              <circle fill-opacity=".5" fill="#FFF" cx="344.828" cy="277.701" r="3.678" />
              <circle fill-opacity=".5" fill="#FFF" cx="276.782" cy="345.747" r="3.678" />
              <circle fill-opacity=".5" fill="#FFF" cx="185.747" cy="369.655" r="3.678" />
              <circle fill-opacity=".5" fill="#FFF" cx="95.632" cy="346.667" r="3.678" />
              <circle fill-opacity=".5" fill="#FFF" cx="27.586" cy="277.701" r="3.678" />
              <circle stroke="#0041C5" stroke-width="5" cx="186.667" cy="187.241" r="6.695" />
            </g>
          </g>
        </g>
      </svg>
      <div class="center"></div>
      <div class="second"></div>
      <div class="hour"></div>
      <div class="minute"></div>
    </div>
  </div>

  <script>
    let clockElement = document.querySelector("#analogClock");
    let offset = -90;

    let reducedMotion = false;

    function timer() {
      let date = new Date();

      let milliseconds = date.getMilliseconds();
      let seconds = date.getSeconds();
      let hours = date.getHours();
      let minutes = date.getMinutes();

      seconds += milliseconds / 1000;
      minutes += seconds / 60;
      hours += minutes / 60;

      // Normalize to the 12 hour clock
      if (hours > 12) {
        hours -= 12;
      }

      clockElement.style.setProperty("--seconds", offset + 6 * seconds + "deg");
      clockElement.style.setProperty("--hours", offset + 30 * hours + "deg");
      clockElement.style.setProperty("--minutes", offset + 6 * minutes + "deg");

      requestAnimationFrame(timer);
    }
    timer();

    let reduceMotionQuery = matchMedia("(prefers-reduced-motion)");

    function setAccessibilityState() {
      if (reduceMotionQuery.matches) {
        reducedMotion = true;
      } else { 
        reducedMotion = false;
      }
    }
    setAccessibilityState();

    reduceMotionQuery.addListener(setAccessibilityState);
  </script>
</body>

</html>

As always, if you have any questions about the code or suggestions on how to make it better, let me know.

I am working on step-by-step tutorial on this right now, so expect to see that live in a few days-ish.

Cheers,
Kirupa

2 Likes

And, the tutorial is up: https://www.kirupa.com/html5/totally_awesome_analog_clock.htm

:stuck_out_tongue: