Chronograph Stopwatch with D3.js
A small project to get started with D3.js. It covers some D3 fundamentals like scaling, transition, grouping, shapes, symbols, arcs etc. There is a play/stop button made with symbols to interact with the stopwatch. To reset the stopwatch, mouse over the button for two seconds.
View it at bl.ocks.org
Here are a couple of notes that I found interesting while doing this project:
Grouping related elements
This is really an important concept to group related elements with
g is like the
<div/> tag in html, but it doesn’t have any visual effect on your document. All the child elements inside the
g element inherit attributes from it. We can utilize this fact to achieve a few things:
Translating SVG origin:
It is sometimes difficult to think when the origin is in the top-left corner. I’m used to picture the origin in the center and rotate counter-clockwise. But in SVG, the origin is in top-left corner and rotation occurs in a clockwise manner. You can use
translatetransformation to move the origin to the center.
For example, in the stopwatch, the primary face dial is centered on the svg by
'translate('+ w/2 +','+ h/2 +')',. Here I did a translation on the
gcontainer instead of individual elements. So everything in the group will have the origin at the center. This makes it easy to place the clock needle and the clock the markers.
Rotate elements together:
Similarly, putting elements in the
gelement makes it easy to rotate a group of elements together. The clock needle and wheel are two separate elements, but during animation, I only rotate their
gcontainer and that makes both child elements rotated at the same time:
setInterval with an interval of 10 milliseconds. That would give me a precision of 1/100th of a second. But it didn’t work well. Here is the naive code:
setInterval stops completely. Here’s what MDN says about Firefox:
Starting in Gecko 5.0 (Firefox 5.0 / Thunderbird 5.0 / SeaMonkey 2.2), intervals are clamped to fire no more often than once per second in inactive tabs.
So the fix is to use the system time by
Date.now() and find the total elapsed time from the start of the clock. Also, it doesn’t affect the accuracy if browser tab is inactive.
Another option is to use D3 Timer module instead of
setTimeout. It uses
window.requestAnimationFrame smooth animation, but falls back to setTimeout for larger(24ms) delays. The
tick function above with D3 timeout would look like this: