javascriptcss-transitionstransformreal-time-clock

The Last Tick Animation of the Arms of the Virtual Clock


I am working on a virtual clock with the help of css and javascript. I have used the following transformation to transform the arms of the clock as the time passes. My problem is that when the arm comes to the end of the hour i.e. transitioning from 59th tick to the 1st one, it winds the arms back to the start of the hour really quick instead of keeping its course. In other words at the 59th tick it does -59 instead of adding one and resetting the counter.

function setDate(){

const now = new Date();const seconds = now.getSeconds();
const secondsDegrees = ((seconds/60) * 360) + 90;
secondHand.style.transform = `rotate(${secondsDegrees}deg)`;

const minutes = now.getMinutes();
const minutesDegrees = ((minutes/60) * 360) +90;
minuteHand.style.transform = `rotate(${minutesDegrees}deg)`;

const hours = now.getHours();
const hoursDegrees = ((hours/12) * 360) +90;
hourHand.style.transform = `rotate(${hoursDegrees}deg)`;
}

I tried to solve the problem by adding a if...else statement to disable the transition effect for the last second of the minute. This didn't have any effect on the output. I think the rewind animation is faster as I turn the animation on and off.

if (seconds == 0) {
transition = false;
if(seconds > 0){
transition = true;
} else {
transition = false;
}}

I am adding the part where the transition can be seen in my CSS file.

.hand {
width: 50%; 
height: 6px; 
background: black; 
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); 
}

Solution

  • Try instead check every time the second/minute/hour value, check its absolute value from X past time. Then you will not reset to 0 after 60 but continue to 61, 62 etc. and the animation will look continues.

    const hours = document.getElementById('hours');
    const minutes = document.getElementById('minutes');
    const seconds = document.getElementById('seconds');
    
    function setDate(){
    
       const time = new Date().getTime() / 1000 % (60 * 60 * 12 * 10) - new Date().getTimezoneOffset()*60;
       const secondsDegrees = Math.floor(time) / 60 * 360 - 90;
       seconds.style.setProperty('--rotate', secondsDegrees+'deg');
    
       const minutesDegrees = Math.floor(time / 60) / 60 * 360 -90;
       minutes.style.setProperty('--rotate', minutesDegrees+'deg');
    
       const hoursDegrees = Math.floor(time/60/60) / 12 * 360 -90;
       hours.style.setProperty('--rotate', hoursDegrees+'deg');
    }
    
    setDate();
    setInterval(setDate, 1000);
    #clock {
      width: 100px;
      height: 100px;
      border-radius: 50%;
      border: 2px solid black;
      margin: 10px;
      position: relative;
    }
    
    #hours, #minutes, #seconds {
      position: absolute;
      top: 50%;
      left: 50%;
      transform: translate(-2px, -50%) rotate(var(--rotate, 0));
      transform-origin: 2px 50%;
      border-radius: 4px;
      transition: transform .2s ease;
    }
    
    #hours {
      width: 30px;
      height: 6px;
      background: black;
    }
    
    #minutes {
      width: 45px;
      height: 4px;
      background: #414141;
    }
    
    #seconds {
      width: 45px;
      height: 2px;
      background: #fa48a1;
    }
    <div id="clock">
      <div id="hours"></div>
      <div id="minutes"></div>
      <div id="seconds"></div>
    </div>

    Or even simpler: check the initial values on load, and then increment on with interval:

    const hours = document.getElementById('hours');
    const minutes = document.getElementById('minutes');
    const seconds = document.getElementById('seconds');
    
    const now = new Date();
    let timeSeconds = now.getHours()*60*60+now.getMinutes()*60+now.getSeconds()-1;
    
    function setDate(){
       timeSeconds++;
       seconds.style.setProperty('--rotate', (timeSeconds / 60 * 360 - 90)+'deg');
       minutes.style.setProperty('--rotate', (Math.floor(timeSeconds / 60 ) / 60 * 360 - 90)+'deg');
       hours.style.setProperty('--rotate', (Math.floor(timeSeconds / 60 / 60) / 12 * 360 - 90)+'deg');
    }
    
    setDate();
    setInterval(setDate, 1000);
    #clock {
      width: 100px;
      height: 100px;
      border-radius: 50%;
      border: 2px solid black;
      margin: 10px;
      position: relative;
    }
    
    #hours, #minutes, #seconds {
      position: absolute;
      top: 50%;
      left: 50%;
      transform: translate(-2px, -50%) rotate(var(--rotate, 0));
      transform-origin: 2px 50%;
      border-radius: 4px;
      transition: transform .2s ease;
    }
    
    #hours {
      width: 30px;
      height: 6px;
      background: black;
    }
    
    #minutes {
      width: 45px;
      height: 4px;
      background: #414141;
    }
    
    #seconds {
      width: 45px;
      height: 2px;
      background: #fa48a1;
    }
    <div id="clock">
      <div id="hours"></div>
      <div id="minutes"></div>
      <div id="seconds"></div>
    </div>