Example of clock using d3 configurable timer

In Configurable circular timer with D3 I showed a circular timer made with D3.js . Although it's not really what it's for, this simple example shows how to make a complex viz. This is a clock that shows the animated hours/mins and seconds using only the timer and some configuration options. It also demonstrates the main features of the timer, such as pausing, resuming and using promises to communicate time expiry and cancellation.

You can use the codepen below to run it and take a look at the code. Fork it an play around with the options to understand how it works.


Clock walkthrough


The hours are animated in smaller circle, minutes the middle one, and seconds by the external viz filling up. I needed quite a few animation elements to make this one. 

Here's the default options.
var clockOptions = {
  duration: 60 * 1000,
  values: {
    classes: "mu--text-caption",
    styles: "font-size:.7em;text-anchor:middle;",
    decorate: function(d) {
      return d3.time.format("%X")(new Date());
    }
  }
};

Each transition is 60 seconds - the time it takes for the second timer to fill up. The time is formatted by a decorator which is called at every tick and returns the current time. 

Here's the clock data.
function getClockData() {
  var now = new Date();
  return [{
      dataName: 'hoursShadow',
      start: {
        fill: '#B3E5FC',
        outerRatio: .7,
        innerRatio: .55,
        angle: 0
      },
      finish: {
        fill: '#B3E5FC',
        outerRatio: .7,
        innerRatio: .55,
        angle: now.getHours() / 24
      },
      immediate: {
        angle: true
      }
    }, {
      dataName: 'minutesShadow',
      start: {
        fill: '#F0F4C3',
        outerRatio: .85,
        innerRatio: .7,
        angle: 0
      },
      finish: {
        fill: '#F0F4C3',
        outerRatio: .85,
        innerRatio: .7,
        angle: now.getMinutes() / 60
      },
      immediate: {
        angle: true
      }
    }, {
      dataName: "hours",
      start: {
        angle: now.getHours() / 24,
        outerRatio: .7,
        innerRatio: .55,
        fill: '#B3E5FC'
      },
      finish: {
        angle: 1/60/24 + (now.getHours() / 24),
        outerRatio: .7,
        innerRatio: .55,
        fill: '#B3E5FC'
      }
    }, {
      dataName: "minutes",
      start: {
        angle: now.getMinutes() / 60,
        outerRatio: .85,
        innerRatio: .7,
        fill: '#F0F4C3'
      },
      finish: {
        angle: 1/60 + (now.getMinutes() / 60),
        outerRatio: .85,
        innerRatio: .7,
        fill: '#F0F4C3'
      }
    }, {
      dataName: "seconds",
      start: {
        angle: now.getSeconds() / 60,
        outerRatio: 2,
        innerRatio: .85,
        fill: '#E1BEE7'
      },
      finish: {
        angle: 1 + (now.getSeconds() / 60),
        outerRatio: 2,
        innerRatio: .85,
        fill: '#FFEB3B'
      },
      values: {
        show: true
      },
    }

  ];
}

The shadow elements are used to show the hours and minutes already passed up to the current time - so their start angles are 0 and finish angles are the start time of the cycles of animation. The hour and minute elements start at the current time and animate 60 seconds worth of hours and minutes. I've used the same color for the shadow elements as I did for the main. Another alternative would be to modify the colors used for hours and minutes and make the start angle the time that the clock was first shown (as opposed to the start of the current animation cycle) - why not try to figure that out with the codepen example. It's just a couple of small changes

The code

Since this is all configured, the code is simply the orchestration of 60 second cycles and the handling of clicks.
var timer = new DashTimer('#clock').init(clockOptions);
endless();

function endless() {
  timer.setData(getClockData());
  timer.start()
    .then(
      function(expired) {
        endless();
      },
      function(cancelled) {
        d3.select('#result').text('cancelled');
      }
    )
}

d3.select('#cancel').on("click", function() {
  timer.cancel();
});
d3.select('#pause').on("click", function() {
  timer.pause();
});
d3.select('#resume').on("click", function() {
  timer.resume();
});
d3.select('#start').on("click", function() {
  endless();
});

Have fun!




For more like this, see Google Apps Scripts snippets. Why not join our forumfollow the blog or follow me on twitter to ensure you get updates when they are available. 




Comments