HTML Code:

<div class="demo-container clocks active bounce">
  <article class="clock station js-new-york">
    <section class="label">New York</section>
    <section class="hours-container">
      <section class="hours"></section>
    </section>
    <section class="minutes-container">
      <section class="minutes"></section>
    </section>
    <section class="seconds-container">
      <section class="seconds"></section>
    </section>
  </article>
  <article class="clock station js-london">
    <section class="label">London</section>
    <section class="hours-container">
      <section class="hours"></section>
    </section>
    <section class="minutes-container">
      <section class="minutes"></section>
    </section>
    <section class="seconds-container">
      <section class="seconds"></section>
    </section>
  </article>
  <article class="clock station js-tokyo">
    <section class="label">Tokyo</section>
    <section class="hours-container">
      <section class="hours"></section>
    </section>
    <section class="minutes-container">
      <section class="minutes"></section>
    </section>
    <section class="seconds-container">
      <section class="seconds"></section>
    </section>
  </article>
</div>

 

CSS Code:

body {
  margin: 0;
}

.demo-container.clocks {
  background: #3cd19e;
  padding: 0;
  width: 100%;
  height: 100vh;
  margin: 0;
  overflow: hidden;
}

.clock {
  border-radius: 50%;
  background: radial-gradient(#000, #000 0.1em, #fff 0.1em, #fff), #fff;
  display: inline-block;
  margin: 1%;
  padding-bottom: 31%;
  position: relative;
  top: 50%;
  width: 31%;
  opacity: 0;
  transform: translateY(-40%);
}

// Put in a no-js alternative so they're not invisible

.clock.show {
  opacity: 1;
  transform: translateY(-50%);
  transition: all 2.5s 0.5s cubic-bezier(.12,1.03,.34,1);
}

.clock::after {
  background: red;
  border-radius: 50%;
  content: "";
  position: absolute;
  left: 50%;
  top: 50%;
  transform: translate(-50%, -50%);
  width: 4%;
  height: 4%;
  z-index: 10;
}

.minutes-container, .hours-container, .seconds-container {
  position: absolute;
  top: 0;
  right: 0;
  bottom: 0;
  left: 0;
}

.hours-container {
  animation: rotate 43200s infinite linear;
}

.linear {
  .minutes-container {
    animation: rotate 3600s infinite linear;
  }
  .seconds-container {
    animation: rotate 60s infinite linear;
  }
}

.steps {
  .minutes-container {
    animation: rotate 3600s infinite steps(60);
  }
  .seconds-container {
    animation: rotate 60s infinite steps(60);
  }
}

.local.steps {
  .minutes-container {
    animation: none;
  }
}

.bounce {
  .minutes-container {
    transition: transform 0.3s cubic-bezier(.4,2.08,.55,.44);
  }
  .seconds-container {
    transition: transform 0.2s cubic-bezier(.4,2.08,.55,.44);
  }
}

.hours {
  background: #000;
  width: 3.5%;
  height: 40%;
  position: absolute;
  left: 48.25%;
  top: 22%;
  transform-origin: 50% 71%;
}

.minutes {
  background: #000;
  width: 3.5%;
  height: 55%;
  position: absolute;
  left: 48.25%;
  top: 7%;
  transform-origin: 50% 78.5%;
}

.seconds {
  background: red;
  width: 1.5%;
  height: 42%;
  position: absolute;
  left: 49.25%;
  top: 20%;
  transform-origin: 50% 71%;
  z-index: 8;
}

.label {
  background: #fff;
  border-radius: 0.25em;
  color: #000;
  font-family: MyriadPro-Regular, 'Myriad Pro Regular', MyriadPro, 'Myriad Pro', Helvetica, Arial, sans-serif;
  font-size: 1em;
  font-weight: bold;
  text-transform: uppercase;
  padding: 0.5em 0.75em 0.25em;
  position: absolute;
  top: -4em;
  left: 50%;
  transform: translate(-50%, 0);
}

@keyframes rotate {
  100% {
    transform: rotateZ(360deg);
  }
}

.clock.station {
  background: #fff url(//cssanimation.rocks/assets/images/posts/clocks/station_clock.svg) no-repeat center;
  background-size: 95%;
  box-shadow: 0 0 0.5em rgba(0,0,0,0.2) inset;
}

.clock.station .seconds::before {
  background: red;
  border-radius: 50%;
  content: "";
  position: absolute;
  top: -9%;
  left: -200%;
  height: 18%;
  width: 500%;
}

.clock.ios7 {
  background: #fff url(/assets/images/posts/clocks/ios_clock.svg) no-repeat center;
  background-size: 88%;
}

.clock.ios7:before {
  background: black;
  border-radius: 50%;
  content: "";
  position: absolute;
  left: 50%;
  top: 50%;
  transform: translate(-50%, -50%);
  width: 6%;
  height: 6%;
  z-index: 0;
}

.clock.ios7:after {
  width: 2%;
  height: 2%;
}

.clock.ios7 .seconds {
  border-radius: 200%/10%;
  height: 30%;
  left: 49.5%;
  top: 20%;
  width: 1%;
  transform-origin: 50% 100%;
}

.clock.ios7 .minutes {
  border-radius: 150%/10%;
  width: 2%;
  height: 35%;
  left: 49%;
  top: 15%;
  transform-origin: 50% 100%;
}

.clock.ios7 .hours {
  border-radius: 85%/10%;
  width: 2%;
  height: 20%;
  left: 49%;
  top: 30%;
  transform-origin: 50% 100%;
}


.clock.simple {
  background: #fff url(/assets/images/posts/clocks/ios_clock.svg) no-repeat center;
  background-size: 88%;
}

.clock.simple:after {
  background-color: #000;
  width: 5%;
  height: 5%;
}

.clock.simple .seconds {
  background-color: #000;
  height: 45%;
  left: 49.5%;
  top: 14%;
  width: 1%;
  transform-origin: 50% 80%;
}

.clock.simple .minutes {
  width: 2%;
  height: 40%;
  left: 49%;
  top: 10%;
  transform-origin: 50% 100%;
}

.clock.simple .hours {
  width: 2.5%;
  height: 20%;
  left: 48.75%;
  top: 30%;
  transform-origin: 50% 100%;
}

.hours.angled {
  transform: rotateZ(-40deg);
}

.minutes.angled {
  transform: rotateZ(40deg);
}

 

JS Code:

/*
 *  Main function to set the clock times
 */
(function() {
  // Initialise the locale-enabled clocks
  initInternationalClocks();
  // Initialise any local time clocks
  initLocalClocks();
  // Start the seconds container moving
  moveSecondHands();
  // Set the intial minute hand container transition, and then each subsequent step
  setUpMinuteHands();
})();

/*
 *  Set up an entry for each locale of clock we want to use
 */
 function getTimes() {
  moment.tz.add([
    'Eire|GMT IST|0 -10|01010101010101010101010|1BWp0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00',
    'Asia/Tokyo|JST|-90|0|',
    'America/New_York|EST EDT|50 40|0101|1Lz50 1zb0 Op0'
    ]);
  var now = new Date();
  // Set the time manually for each of the clock types we're using
  var times = [
    {
      jsclass: 'js-tokyo',
      jstime: moment.tz(now, "Asia/Tokyo")
    },
    {
      jsclass: 'js-london',
      jstime: moment.tz(now, "Eire")
    },
    {
      jsclass: 'js-new-york',
      jstime: moment.tz(now, "America/New_York")
    }
  ];
  return times;
}

/*
 * Set up the clocks that use moment.js
 */
function initInternationalClocks() {
  // Initialise the clock settings and the three times
  var times = getTimes();
  for (i = 0; i < times.length; ++i) {
    var hours = times[i].jstime.format('h');
    var minutes = times[i].jstime.format('mm');
    var seconds = times[i].jstime.format('ss');

    var degrees = [
      {
        hand: 'hours',
        degree: (hours * 30) + (minutes / 2)
      },
      {
        hand: 'minutes',
        degree: (minutes * 6)
      },
      {
        hand: 'seconds',
        degree: (seconds * 6)
      }
    ];
    for (var j = 0; j < degrees.length; j++) {
      var elements = document.querySelectorAll('.active .' + times[i].jsclass + ' .' + degrees[j].hand);
      for (var k = 0; k < elements.length; k++) {
        	elements[k].style.webkitTransform = 'rotateZ('+ degrees[j].degree +'deg)';
          elements[k].style.transform = 'rotateZ('+ degrees[j].degree +'deg)';
          // If this is a minute hand, note the seconds position (to calculate minute position later)
          if (degrees[j].hand === 'minutes') {
            elements[k].parentNode.setAttribute('data-second-angle', degrees[j + 1].degree);
          }
      }
    }
  }
  // Add a class to the clock container to show it
  var elements = document.querySelectorAll('.clock');
  for (var l = 0; l < elements.length; l++) {
    elements[l].className = elements[l].className + ' show';
  }
}

/*
 * Starts any clocks using the user's local time
 */
function initLocalClocks() {
  // Get the local time using JS
  var date = new Date;
  var seconds = date.getSeconds();
  var minutes = date.getMinutes();
  var hours = date.getHours();

  // Create an object with each hand and it's angle in degrees
  var hands = [
    {
      hand: 'hours',
      angle: (hours * 30) + (minutes / 2)
    },
    {
      hand: 'minutes',
      angle: (minutes * 6)
    },
    {
      hand: 'seconds',
      angle: (seconds * 6)
    }
  ];
  // Loop through each of these hands to set their angle
  for (var j = 0; j < hands.length; j++) {
    var elements = document.querySelectorAll('.local .' + hands[j].hand);
    for (var k = 0; k < elements.length; k++) {
        elements[k].style.transform = 'rotateZ('+ hands[j].angle +'deg)';
        // If this is a minute hand, note the seconds position (to calculate minute position later)
        if (hands[j].hand === 'minutes') {
          elements[k].parentNode.setAttribute('data-second-angle', hands[j + 1].angle);
        }
    }
  }
}

/*
 * Move the second containers
 */
function moveSecondHands() {
  var containers = document.querySelectorAll('.bounce .seconds-container');
  setInterval(function() {
    for (var i = 0; i < containers.length; i++) {
      if (containers[i].angle === undefined) {
        containers[i].angle = 6;
      } else {
        containers[i].angle += 6;
      }
      containers[i].style.webkitTransform = 'rotateZ('+ containers[i].angle +'deg)';
      containers[i].style.transform = 'rotateZ('+ containers[i].angle +'deg)';
    }
  }, 1000);
  for (var i = 0; i < containers.length; i++) {
    // Add in a little delay to make them feel more natural
    var randomOffset = Math.floor(Math.random() * (100 - 10 + 1)) + 10;
    containers[i].style.transitionDelay = '0.0'+ randomOffset +'s';
  }
}

/*
 * Set a timeout for the first minute hand movement (less than 1 minute), then rotate it every minute after that
 */
function setUpMinuteHands() {
  // More tricky, this needs to move the minute hand when the second hand hits zero
  var containers = document.querySelectorAll('.minutes-container');
  var secondAngle = containers[containers.length - 1].getAttribute('data-second-angle');
  console.log(secondAngle);
  if (secondAngle > 0) {
    // Set a timeout until the end of the current minute, to move the hand
    var delay = (((360 - secondAngle) / 6) + 0.1) * 1000;
    console.log(delay);
    setTimeout(function() {
      moveMinuteHands(containers);
    }, delay);
  }
}

/*
 * Do the first minute's rotation, then move every 60 seconds after
 */
function moveMinuteHands(containers) {
  for (var i = 0; i < containers.length; i++) {
    containers[i].style.webkitTransform = 'rotateZ(6deg)';
    containers[i].style.transform = 'rotateZ(6deg)';
  }
  // Then continue with a 60 second interval
  setInterval(function() {
    for (var i = 0; i < containers.length; i++) {
      if (containers[i].angle === undefined) {
        containers[i].angle = 12;
      } else {
        containers[i].angle += 6;
      }
      containers[i].style.webkitTransform = 'rotateZ('+ containers[i].angle +'deg)';
      containers[i].style.transform = 'rotateZ('+ containers[i].angle +'deg)';
    }
  }, 60000);
}

 

Demo:

See the Pen World clocks by Donovan Hutchinson (@donovanh) on CodePen.