Progress Clock
Progress Clock

Hello guys, In this tutorial, we will create a Progress Clock. Our clock will show the progress of each second, minute, hour, and month.

Here in the above image, as you can see the bar is increasing with seconds. Other bars are also increasing like this.

Progress Clock

For creating our progress clock, we need to create three files called, index.html, style.css, and script.js. Let’s start with our index.js file.

index.html

<!DOCTYPE html>
<head>
  <title>Progress Clock</title>
  <link rel="stylesheet" href="style.css">
</head>
<body>
  <div id="clock" class="progress-clock">
    <button class="progress-clock__time-date" data-group="d" type="button">
      <small data-unit="w">Sunday</small><br>
      <span data-unit="mo">January</span>
      <span data-unit="d">1</span>
    </button>
    <button class="progress-clock__time-digit" data-unit="h" data-group="h" type="button">12</button><span class="progress-clock__time-colon">:</span><button class="progress-clock__time-digit" data-unit="m" data-group="m" type="button">00</button><span class="progress-clock__time-colon">:</span><button class="progress-clock__time-digit" data-unit="s" data-group="s" type="button">00</button>
    <span class="progress-clock__time-ampm" data-unit="ap">AM</span>
    <svg class="progress-clock__rings" width="256" height="256" viewBox="0 0 256 256">
      <defs>
        <linearGradient id="pc-red" x1="1" y1="0.5" x2="0" y2="0.5">
          <stop offset="0%" stop-color="#f953c6" />
          <stop offset="100%" stop-color="#b91d73" />
        </linearGradient>
        <linearGradient id="pc-yellow" x1="1" y1="0.5" x2="0" y2="0.5">
          <stop offset="0%" stop-color="#00B4DB" />
          <stop offset="100%" stop-color="#0083B0" />
        </linearGradient>
        <linearGradient id="pc-blue" x1="1" y1="0.5" x2="0" y2="0.5">
          <stop offset="0%" stop-color="#8E2DE2" />
          <stop offset="100%" stop-color="#4A00E0" />
        </linearGradient>
        <linearGradient id="pc-purple" x1="1" y1="0.5" x2="0" y2="0.5">
          <stop offset="0%" stop-color="#FF416C" />
          <stop offset="100%" stop-color="#FF4B2B" />
        </linearGradient>
      </defs>
      <!-- Days of Month -->
      <g data-units="d">
        <circle class="progress-clock__ring" cx="128" cy="128" r="74" fill="none" opacity="0.1" stroke="url(#pc-red)" stroke-width="12" />
        <circle class="progress-clock__ring-fill" data-ring="mo" cx="128" cy="128" r="74" fill="none" stroke="url(#pc-red)" stroke-width="12" stroke-dasharray="465 465" stroke-dashoffset="465" stroke-linecap="round" transform="rotate(-90,128,128)" />
      </g>
      <!-- Hours of Day -->
      <g data-units="h">
        <circle class="progress-clock__ring" cx="128" cy="128" r="90" fill="none" opacity="0.1" stroke="url(#pc-yellow)" stroke-width="12" />
        <circle class="progress-clock__ring-fill" data-ring="d" cx="128" cy="128" r="90" fill="none" stroke="url(#pc-yellow)" stroke-width="12" stroke-dasharray="565.5 565.5" stroke-dashoffset="565.5" stroke-linecap="round" transform="rotate(-90,128,128)" />
      </g>
      <!-- Minutes of Hour -->
      <g data-units="m">
        <circle class="progress-clock__ring" cx="128" cy="128" r="106" fill="none" opacity="0.1" stroke="url(#pc-blue)" stroke-width="12" />
        <circle class="progress-clock__ring-fill" data-ring="h" cx="128" cy="128" r="106" fill="none" stroke="url(#pc-blue)" stroke-width="12" stroke-dasharray="666 666" stroke-dashoffset="666" stroke-linecap="round" transform="rotate(-90,128,128)" />
      </g>
      <!-- Seconds of Minute -->
      <g data-units="s">
        <circle class="progress-clock__ring" cx="128" cy="128" r="122" fill="none" opacity="0.1" stroke="url(#pc-purple)" stroke-width="12" />
        <circle class="progress-clock__ring-fill" data-ring="m" cx="128" cy="128" r="122" fill="none" stroke="url(#pc-purple)" stroke-width="12" stroke-dasharray="766.5 766.5" stroke-dashoffset="766.5" stroke-linecap="round" transform="rotate(-90,128,128)" />
      </g>
    </svg>
  </div>
  <script type="text/javascript" src="script.js"></script>
</body>
</html>

Here we have created our basic structure using HTML, now we will style our HTML code.

style.css

* {
  border: 0;
  box-sizing: border-box;
  margin: 0;
  padding: 0;
}
:root {
  --bgColor: #ffffff;
}
body, button {
  color: #b91d73;
  font: 1em;
}
body {
  background-color: var(--bgColor);
  height: 100vh;
  display: grid;
  place-items: center;
}

.progress-clock {
  display: grid;
  justify-content: center;
  align-content: center;
  position: relative;
  text-align: center;
  width: 16em;
  height: 16em;
}
.progress-clock__time-date,
.progress-clock__time-digit,
.progress-clock__time-colon,
.progress-clock__time-ampm {
  transition: color 0.2s linear;
  -webkit-user-select: none;
  -moz-user-select: none;
  user-select: none;
}
.progress-clock__time-date,
.progress-clock__time-digit {
  background: transparent;
}
.progress-clock__time-date,
.progress-clock__time-ampm {
  grid-column: 1 / 6;
}
.progress-clock__time-date {
  font-size: 0.75em;
  line-height: 1.33;
}
.progress-clock__time-digit,
.progress-clock__time-colon {
  font-size: 2em;
  font-weight: 400;
  grid-row: 2;
}
.progress-clock__time-colon {
  line-height: 1.275;
}
.progress-clock__time-ampm {
  cursor: default;
  grid-row: 3;
}
.progress-clock__rings {
  display: block;
  position: absolute;
  top: 0;
  left: 0;
  width: 100%;
  height: 100%;
  z-index: -1;
}
.progress-clock__ring {
  opacity: 0.1;
}
.progress-clock__ring-fill {
  transition:
    opacity 0s 0.3s linear,
    stroke-dashoffset 0.3s ease-in-out;
}
.progress-clock__ring-fill--360 {
  opacity: 0;
  stroke-dashoffset: 0;
  transition-duration: 0.3s;
}


Here our basic structure with style is ready now we will use javascript to get the date and time. We will use it also to run other tasks. Let’s start our work.

script.js

window.addEventListener("DOMContentLoaded",() => {
  const clock = new ProgressClock("#clock");
});

class ProgressClock {
  constructor(qs) {
    this.el = document.querySelector(qs);
    this.time = 0;
    this.updateTimeout = null;
    this.ringTimeouts = [];
    this.update();
  }
  getDayOfWeek(day) {
    switch (day) {
      case 1:
        return "Monday";
      case 2:
        return "Tuesday";
      case 3:
        return "Wednesday";
      case 4:
        return "Thursday";
      case 5:
        return "Friday";
      case 6:
        return "Saturday";
      default:
        return "Sunday";
    }
  }
  getMonthInfo(mo,yr) {
    switch (mo) {
      case 1:
        return { name: "February", days: yr % 4 === 0 ? 29 : 28 };
      case 2:
        return { name: "March", days: 31 };
      case 3:
        return { name: "April", days: 30 };
      case 4:
        return { name: "May", days: 31 };
      case 5:
        return { name: "June", days: 30 };
      case 6:
        return { name: "July", days: 31 };
      case 7:
        return { name: "August", days: 31 };
      case 8:
        return { name: "September", days: 30 };
      case 9:
        return { name: "October", days: 31 };
      case 10:
        return { name: "November", days: 30 };
      case 11:
        return { name: "December", days: 31 };
      default:
        return { name: "January", days: 31 };
    }
  }
  update() {
    this.time = new Date();

    if (this.el) {
      // date and time
      const dayOfWeek = this.time.getDay();
      const year = this.time.getFullYear();
      const month = this.time.getMonth();
      const day = this.time.getDate();
      const hr = this.time.getHours();
      const min = this.time.getMinutes();
      const sec = this.time.getSeconds();
      const dayOfWeekName = this.getDayOfWeek(dayOfWeek);
      const monthInfo = this.getMonthInfo(month,year);
      const m_progress = sec / 60;
      const h_progress = (min + m_progress) / 60;
      const d_progress = (hr + h_progress) / 24;
      const mo_progress = ((day - 1) + d_progress) / monthInfo.days;
      const units = [
        {
          label: "w",
          value: dayOfWeekName
        },
        {
          label: "mo",
          value: monthInfo.name,
          progress: mo_progress
        },
        {
          label: "d", 
          value: day,
          progress: d_progress
        },
        {
          label: "h", 
          value: hr > 12 ? hr - 12 : hr,
          progress: h_progress
        },
        {
          label: "m", 
          value: min < 10 ? "0" + min : min,
          progress: m_progress
        },
        {
          label: "s", 
          value: sec < 10 ? "0" + sec : sec
        },
        {
          label: "ap",
          value: hr > 12 ? "PM" : "AM"
        }
      ];

      // flush out the timeouts
      this.ringTimeouts.forEach(t => {
        clearTimeout(t);
      });
      this.ringTimeouts = [];

      // update the display
      units.forEach(u => {
        // rings
        const ring = this.el.querySelector(`[data-ring="${u.label}"]`);

        if (ring) {
          const strokeDashArray = ring.getAttribute("stroke-dasharray");
          const fill360 = "progress-clock__ring-fill--360";

          if (strokeDashArray) {
            // calculate the stroke
            const circumference = +strokeDashArray.split(" ")[0];
            const strokeDashOffsetPct = 1 - u.progress;

            ring.setAttribute(
              "stroke-dashoffset",
              strokeDashOffsetPct * circumference
            );

            // add the fade-out transition, then remove it
            if (strokeDashOffsetPct === 1) {
              ring.classList.add(fill360);

              this.ringTimeouts.push(
                setTimeout(() => {
                  ring.classList.remove(fill360);
                }, 600)
              );
            }
          }
        }

        // digits
        const unit = this.el.querySelector(`[data-unit="${u.label}"]`);

        if (unit)
          unit.innerText = u.value;
      });
    }

    clearTimeout(this.updateTimeout);
    this.updateTimeout = setTimeout(this.update.bind(this),1e3);
  }
}

Hope you guys enjoyed this tutorial!

If you are having any issues with the code, then you can visit our GitHub Account. Click Here!

You Might Like This:

Our Courses:

HTML – Beginner to Advance

CSS – Beginner to Advance


Click to rate this post!
[Total: 0 Average: 0]

Oh, hi there 👋 It’s nice to meet you.

Sign up to receive awesome content in your inbox, every week.

We don’t spam! Read our privacy policy for more info.

Leave a Reply