1

I am building a css3 clock and applied a transition to its second hand but it circles back at 0deg? I tried to add a one-time event listener to remove transition when it reaches 360 degrees in order for the second hand to jump instantly from 360 to the start, but it did not work. Is there any way to correct this, except incrementing the degrees infinitely.

var secHand = document.querySelector('div div:first-child');
var minHand = document.querySelector('div:nth-child(2)');
var hourHand = document.querySelector('div:nth-child(3)');
var myVar = setInterval(myTimer, 1000);

function myTimer() {
  var d = new Date();
  function updateHands() {
    secHand.style.transform = `rotate(${d.getSeconds()*6}deg)`;
    minHand.style.transform = `rotate(${d.getMinutes()*6}deg)`;
    hourHand.style.transform = `rotate(${d.getHours()*(360/12) + d.getMinutes()*6*6/360}deg)`;
  }
  // I tried this but I don't know how to make it work properly.
  if (d.getSeconds() == 0) {
    secHand.style.transform = 'rotate(360deg)';
    secHand.addEventListener('transitionend', function(e){
      e.target.removeEventListener(e.type, arguments.callee);
      this.style.transition = 'transform 0s';
      updateHands();
      this.style.transition = 'transform 0.5s';
    })
  }
  else updateHands();
}
body {
  width: 100%;
  height: 100%;
  margin: 0;
  padding: 0;
  overflow: hidden;
  background: url('https://stmed.net/sites/default/files/sky-wallpapers-28043-2711012.jpg'), linear-gradient(to bottom, #1e528e 0%, #265889 50%, #9da671 100%);
  background-repeat: no-repeat;
  background-attachment: fixed;

}
div {
  border-radius: 50%;
}
body > div {
  border: 20px solid white;
  width: 300px;
  height: 300px;
  border-radius: 50%;
  position: absolute;
  top: 0;
  right: 0;
  left: 0;
  bottom: 0;
  margin: auto;
}
div div:first-child {
  margin: auto;
  width: 3px;
  background-color: black;
  height: 49%;
  position: absolute;
  left: 0;
  right: 0;
  bottom: 50%;
  transform: rotate(180deg);
  transform-origin: 50% 100%;
  transition: transform 0.5s;
  transition-timing-function: cubic-bezier(0.1, 2.7, 0.58, 1) !important;
}
div:nth-child(2) {
  width: 5px;
  background-color: black;
  height: 46%;
  position: absolute;
  left: 0;
  right: 0;
  bottom: 50%;
  margin: auto;
  transform: rotate(360deg);
  transform-origin: 50% 100%;
  transition: transform 2s;
  
}
div:nth-child(3) {
  width: 5px;
  background-color: black;
  height: 43%;
  position: absolute;
  left: 0;
  right: 0;
  bottom: 50%;
  margin: auto;
  transform: rotate(360deg);
  transform-origin: 50% 100%;
  transition: transform 3s;
  
}
<div>
  <div></div>
  <div></div>
  <div></div>
</div>
Watch the snippet, when the second hand reaches 60sec it circles back.
Nasridean
  • 45
  • 6
  • Not sure what you mean by "it rotates back at 0deg" ? – Bemmu Dec 12 '18 at 07:45
  • I mean when the seconds hand changes its degree from 354deg to 0deg, it circles back due to transition. – Nasridean Dec 12 '18 at 07:51
  • I tried to turn the transition off at this moment but couldn't make it work properly. – Nasridean Dec 12 '18 at 07:53
  • Would be easier to attempt an answer if there was a JSfiddle. I'm feeling reluctant to try to recreate your layout to attempt an answer. – Bemmu Dec 12 '18 at 08:04
  • @Bemmu I added. – Nasridean Dec 12 '18 at 12:11
  • I know what you mean, Ive just seen it. You mean that stutter animation when it resets back? Its because you transform it back to 0 degrees so it goes from 360 to 0 and then again and agian. You would need to add a counter and rotate it 360x ammount of times it went trough, for example your second rotation would be 720 and so on. or you could disable animations for brief second so it would refresh. – Thomas J. Dec 12 '18 at 12:12
  • @Comirdc I know your first solution, but I want the second solution by disabling animations. As shown in the code I tried to disable it by an event listener, but it didn't work. And by the way, which one consumes less resources, incrementing the degrees continuously or resetting? – Nasridean Dec 12 '18 at 12:21
  • Hmmm, you could make a sync function or a simple callback, so when its 360 deg. disable animation -> Reset it to 0, then on callback, enable animation again. Since now I assume its disabling and enabling anim (js is async) – Thomas J. Dec 12 '18 at 12:32
  • @Comirdc I think a sync function consumes much of resources. As for callbacks, could you show an example? – Nasridean Dec 12 '18 at 13:02
  • @Nasridean Ive responded an answer, since I guess using a callback is your only alternative if you dont wanna use x timer multiply :) Ofcourse Ive made callback to be with extra func, but you could modify your updateHands to have a static callback, to skip one func. – Thomas J. Dec 12 '18 at 13:10
  • @Nasridean Thanks for adding the layout snippet. I added an answer that works by disabling transitions as you asked. I hope you find it solves your problem. – Bemmu Dec 12 '18 at 16:10

3 Answers3

0

Id make a callback for example somthing similar

var secHand = document.querySelector('div div:first-child');
var minHand = document.querySelector('div:nth-child(2)');
var hourHand = document.querySelector('div:nth-child(3)');
var myVar = setInterval(myTimer, 1000);

function myTimer() {
  var d = new Date();
  function updateHands() {
    secHand.style.transform = `rotate(${d.getSeconds()*6}deg)`;
    minHand.style.transform = `rotate(${d.getMinutes()*6}deg)`;
    hourHand.style.transform = `rotate(${d.getHours()*(360/12) + d.getMinutes()*6*6/360}deg)`;
  }
  // I tried this but I don't know how to make it work properly.
  if (d.getSeconds() == 0) {
    secHand.style.transform = 'rotate(360deg)';
    secHand.addEventListener('transitionend', function(e){
      e.target.removeEventListener(e.type, arguments.callee);
      this.style.transition = 'transform 0s';
      var localyRechunk = function(callBack) {
        updateHands();
        callBack();
      };

      localyRechunk(function() {
          console.log('give back the transition');
      });

    })
  }
  else updateHands();
}

made a fiddle aswell: https://jsfiddle.net/3qvyxmdu/ I just didint complete it so it would give back the transition effect. But simple callback. You can read about it in hare.

Thomas J.
  • 593
  • 8
  • 21
0

I would suggest using a counter to keep track of how may revolutions have occurred and use this figure to workout what the next true revolution is.

var secHand = document.querySelector('div div:first-child');
var minHand = document.querySelector('div:nth-child(2)');
var hourHand = document.querySelector('div:nth-child(3)');
var myVar = setInterval(myTimer, 1000);
var secRounds = 0;

function myTimer() {
  var d = new Date();
  function updateHands() {
    secHand.style.transform = `rotate(${d.getSeconds()*6 + 360*secRounds }deg)`;
    minHand.style.transform = `rotate(${d.getMinutes()*6}deg)`;
    hourHand.style.transform = `rotate(${d.getHours()*(360/12) + d.getMinutes()*6*6/360}deg)`;
  }
  // I tried this but I don't know how to make it work properly.
  if (d.getSeconds() == 0) {
    secRounds ++;
  }
 updateHands();
}
body {
  width: 100%;
  height: 100%;
  margin: 0;
  padding: 0;
  overflow: hidden;
  background: url('https://stmed.net/sites/default/files/sky-wallpapers-28043-2711012.jpg'), linear-gradient(to bottom, #1e528e 0%, #265889 50%, #9da671 100%);
  background-repeat: no-repeat;
  background-attachment: fixed;

}
div {
  border-radius: 50%;
}
body > div {
  border: 20px solid white;
  width: 300px;
  height: 300px;
  border-radius: 50%;
  position: absolute;
  top: 0;
  right: 0;
  left: 0;
  bottom: 0;
  margin: auto;
}
div div:first-child {
  margin: auto;
  width: 3px;
  background-color: black;
  height: 49%;
  position: absolute;
  left: 0;
  right: 0;
  bottom: 50%;
  transform: rotate(180deg);
  transform-origin: 50% 100%;
  transition: transform 0.5s;
  transition-timing-function: cubic-bezier(0.1, 2.7, 0.58, 1) !important;
}
div:nth-child(2) {
  width: 5px;
  background-color: black;
  height: 46%;
  position: absolute;
  left: 0;
  right: 0;
  bottom: 50%;
  margin: auto;
  transform: rotate(360deg);
  transform-origin: 50% 100%;
  transition: transform 2s;
  
}
div:nth-child(3) {
  width: 5px;
  background-color: black;
  height: 43%;
  position: absolute;
  left: 0;
  right: 0;
  bottom: 50%;
  margin: auto;
  transform: rotate(360deg);
  transform-origin: 50% 100%;
  transition: transform 3s;
  
}
<div>
  <div></div>
  <div></div>
  <div></div>
</div>

ypoulakas
  • 401
  • 4
  • 7
  • If you read comments, he said that he knows that solution, and all ready tried it, hes searching for alternatives. – Thomas J. Dec 12 '18 at 13:45
0

I believe this is what you were looking for, this works by temporarily disabling transitions. I also realized that you only ever want to rotate forwards, so this just advances the seconds hand by 6 degrees.

When the minute changes it rotates from 354 to 360 degrees, then 0 degrees to 6 degrees. You don't see it going from 360 to 0, because the transitions are disabled then.

Disabling transitions was explained here: What is the cleanest way to disable CSS transition effects temporarily?

I was too lazy to implement this for all three hands, and I figured you'd get the idea easier from this simplified example anyway.

var secHand = document.querySelector('div div:first-child');
setInterval(myTimer, 1000);

function myTimer() {
    var d = new Date();

    // Advance seconds hand 6 degrees each second
    var oldDeg = d.getSeconds()*6;
    var newDeg = oldDeg + 6;
    console.log(`Transitioning from ${oldDeg} to ${newDeg}`);

    // Snap to old rotation by disabling transition
    secHand.classList.add('notransition');
    secHand.style.transform = `rotate(${oldDeg}deg)`;
    secHand.offsetHeight; // Trigger reflow

    // Transition to new rotation
    secHand.classList.remove('notransition');
    secHand.style.transform = `rotate(${newDeg}deg)`;
}
body {
  width: 100%;
  height: 100%;
  margin: 0;
  padding: 0;
  overflow: hidden;
  background: url('https://stmed.net/sites/default/files/sky-wallpapers-28043-2711012.jpg'), linear-gradient(to bottom, #1e528e 0%, #265889 50%, #9da671 100%);
  background-repeat: no-repeat;
  background-attachment: fixed;

}
div {
  border-radius: 50%;
}
body > div {
  border: 20px solid white;
  width: 300px;
  height: 300px;
  border-radius: 50%;
  position: absolute;
  top: 0;
  right: 0;
  left: 0;
  bottom: 0;
  margin: auto;
}
div div:first-child {
  margin: auto;
  width: 3px;
  background-color: black;
  height: 49%;
  position: absolute;
  left: 0;
  right: 0;
  bottom: 50%;
  transform: rotate(180deg);
  transform-origin: 50% 100%;
  transition: transform 0.5s;
  transition-timing-function: cubic-bezier(0.1, 2.7, 0.58, 1) !important;
}

.notransition {
  -webkit-transition: none !important;
  -moz-transition: none !important;
  -o-transition: none !important;
  transition: none !important;
}
<div>
  <div></div>
</div>
Bemmu
  • 17,849
  • 16
  • 76
  • 93
  • 1
    Wow, something new to me, this exactly what I was looking for. I just didn't know about reflow and was disabling the transition the naive way. Thank you! – Nasridean Dec 13 '18 at 04:41