Home | Math art in code | Spinning Clock

Page by Murray Bourne, IntMath.com. Last updated: 24 Jun 2018

Spinning Clock

Last updated: 24 June 2018.

This mesmerising rotating clock is made with CSS and javascript.

Code outline

Numbers for the hours, minutes and seconds are contained in DIV elements which are rotated around the center of the circle.

There are various classes assigned to the DIVs, and these do the heavy lifting as far as the rotations and opacity is concerned. Here's a typical example:

  .spinner_m2_1{transform:rotate( 324deg )}

and

  .num div{transition: transform 500ms ease-out, color 500ms ease-out, font-size 500ms ease-out}

In the javascript, the classes are swapped in and out appropriately using code such as:

case "spinner_h1_2":
  rem_class("spinner_h1", "spinner_h1_2");
  add_class("spinner_h1", "spinner_h1_switch");
0
1
2
0
1
2
3
4
5
6
7
8
9
0
1
2
3
4
5
0
1
2
3
4
5
6
7
8
9
0
1
2
3
4
5
0
1
2
3
4
5
6
7
8
9

The complete javascript

var today = new Date();
var sRem = today.getSeconds();
	 

//These functions help add, remove or toggle css classes

function tog_class(id, cl) {
  var elem = document.getElementById(id);
  if (elem == null) {
    return 0;
  }
  if (elem.classList.contains(cl) === true) {
    elem.classList.remove(cl);
  } else {
    elem.classList.add(cl);
  }
}

function add_class(id, cl) {
  var elem = document.getElementById(id);
  if (elem == null) {
    return 0;
  }
  if (elem.classList.contains(cl) !== true) {
    elem.classList.add(cl);
  }
}

function rem_class(id, cl) {
  var elem = document.getElementById(id);
  if (elem == null) {
    return 0;
  }
  if (elem.classList.contains(cl) === true) {
    elem.classList.remove(cl);
  }
}

//This function gets the date and does operations using H/M/S

function startTime() {
  today = new Date();

  var s = today.getSeconds();

  if( sRem != s) {
	  var h = today.getHours();
	  var m = today.getMinutes();	  

	  var h1 = (h - h % 10) / 10;
	  var h2 = h % 10;

	  var m1 = (m - m % 10) / 10;
	  var m2 = m % 10;

	  var s1 = (s - s % 10) / 10;
	  var s2 = s % 10;

	  set_spin_class("s1", s1);
	  set_spin_class("s2", s2);

	  set_spin_class("m1", m1);
	  set_spin_class("m2", m2);

	  set_spin_class("h1", h1);
	  set_spin_class("h2", h2);

  } 
  window.requestAnimationFrame(startTime);
  sRem = s;
}

//This function calls the appropriate class changes

// It receives the type of spinner to change (m1 for example) and changes classes based on the current time

function set_spin_class(target, val) {
  for (i = 0; i < 10; i++) {
    if (i != val) {
      rem_class("spinner_" + target, "spinner_" + target + "_" + i);
      rem_class("num_" + i + "_" + target, "lit");
    }
  }
  add_class("spinner_" + target, "spinner_" + target + "_" + val);
  add_class("num_" + val + "_" + target, "lit");
  if (val === 0) {
    deswitcher(target, val);
  }
}

function switcher(target, val) {
  switch ("spinner_" + target + "_" + val) {
    case "spinner_h1_2":
      rem_class("spinner_h1", "spinner_h1_2");
      add_class("spinner_h1", "spinner_h1_switch");
      break;
    case "spinner_h2_9":
      rem_class("spinner_h2", "spinner_h1_2");
      add_class("spinner_h2", "spinner_h2_switch");
      break;

    case "spinner_m1_5":
      rem_class("spinner_m1", "spinner_m1_5");
      add_class("spinner_m1", "spinner_m1_switch");
      break;
    case "spinner_m2_9":
      rem_class("spinner_m2", "spinner_m2_9");
      add_class("spinner_m2", "spinner_m2_switch");
      break;

    case "spinner_s1_5":
      rem_class("spinner_s1", "spinner_s1_5");
      add_class("spinner_s1", "spinner_s1_switch");
      break;
    case "spinner_s2_9":
      rem_class("spinner_s2", "spinner_s2_9");
      add_class("spinner_s2", "spinner_s2_switch");
      break;
  }
}

function deswitcher(target, val) {
  rem_class("spinner_" + target, "spinner_" + target + "_switch");
}

//Kicks it off

window.onload = function() {
  startTime();
};

Credit

The original script is by Stefanescu Lucian, source: CodePen. I made small changes to the CSS so that it would work within a DIV element, rather than within an entire HTML page, as per the original design, and to change colors.

There was an occasional jerky motion where the clock paused then had to "catch up", so I changed the use of setInterval to window.requestAnimationFrame.