Skip to content

Instantly share code, notes, and snippets.

@jcdcodes
Forked from mbostock/.block
Last active March 2, 2016 14:25
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save jcdcodes/56f08ff3cbdf74caa012 to your computer and use it in GitHub Desktop.
Save jcdcodes/56f08ff3cbdf74caa012 to your computer and use it in GitHub Desktop.
Arc-and-Hands Tween Clock

Forked from mbostock's Arc Tween Clock example.

The first thing I learned with this is that .selectAll() takes a CSS selector as its argument. When dealing with multiple copies of the same element, like the arcs and the red lines here (which are both svg:path elements), if you want to use transitions and separate styling then one must apply class or id attributes or something so that the CSS selectors for the two sets of transitions can be mutually independent.

The second thing is that I learned is that I hate code that puns. False punning I can stand (though the commentary below points out what I think is a false punning bug I fixed), but I'm not smart enough and/or don't have the energy to deal with three or four separate simultaneous meanings of "path" and "line" that all need to be kept in mind when reading and writing the same small chunk of code. I've been known to do this kind of thing in the past, particularly when writing Grails code, so I'll have to start being more careful to avoid creating these kinds of traps for others. On the other hand, it only takes about a second to render each iteration of the code, so some approximation of interactive development is possible. It's not instantaneous, but it's fast enough that one doesn't lose his train of thought while waiting to see how the latest version of the code will render.

<!DOCTYPE html>
<html>
<head>
<script type="text/javascript" src="//d3js.org/d3.v3.min.js"></script>
<style type="text/css">
.arc {
fill-rule: evenodd;
fill: #aaa;
fill-opacity: .7;
stroke: #666;
stroke-width: 1.5px;
}
.hand {
fill: none;
stroke: red;
stroke-width: 1.5px;
}
</style>
</head>
<body>
<script type="text/javascript">
var w = 960,
h = 500,
x = d3.scale.ordinal().domain(d3.range(3)).rangePoints([0, w], 2);
var fields = [
{name: "hours", value: 0, size: 12},
{name: "minutes", value: 0, size: 60},
{name: "seconds", value: 0, size: 60}
];
function angle(d) {
return (d.value / d.size) * 2 * Math.PI;
}
var arc = d3.svg.arc()
.innerRadius(100)
.outerRadius(140)
.startAngle(0)
.endAngle(angle);
var hand = d3.svg.arc()
.innerRadius(10)
.outerRadius(150)
.startAngle(angle)
.endAngle(angle);
var svg = d3.select("body").append("svg:svg")
.attr("width", w)
.attr("height", h)
.append("svg:g")
.attr("transform", "translate(0," + (h / 2) + ")");
setInterval(function() {
var now = new Date();
fields[0].previous = fields[0].value; fields[0].value = now.getHours() % 12;
fields[1].previous = fields[1].value; fields[1].value = now.getMinutes();
fields[2].previous = fields[2].value; fields[2].value = now.getSeconds();
// Smoothly animate between :59 and :00
if (fields[0].value == 0) fields[0].previous = -1;
if (fields[1].value == 0) fields[1].previous = -1;
if (fields[2].value == 0) fields[2].previous = -1;
// Here, we filter not for entries where d.value evaluates to true, but where
// there is a number. Otherwise we don't get to see all three dials when
// it's midnight, noon, or the top of the hour; zero evaluates to false in javascript.
var path = svg.selectAll("path.arc")
.data(fields.filter(function(d) { return d.value > -1; }), function(d) { return d.name; });
var line = svg.selectAll("path.hand")
.data(fields.filter(function(d) { return d.value > -1; }), function(d) { return d.name; });
path.enter().append("svg:path")
.attr("transform", function(d, i) { return "translate(" + x(i) + ",0)"; })
.attr("class", "arc")
.transition()
.ease("elastic")
.duration(100)
.attrTween("d", tweenWith(arc));
path.transition()
.ease("elastic")
.duration(750)
.attrTween("d", tweenWith(arc));
path.exit().transition()
.ease("bounce")
.duration(750)
.attrTween("d", tweenWith(arc))
.remove();
line.enter().append("svg:path")
.attr("transform", function(d, i) { return "translate(" + x(i) + ",0)"; })
.attr("class", "hand")
.transition()
.ease("elastic")
.duration(100)
.attrTween("d", tweenWith(hand))
;
line.transition()
.ease("elastic")
.duration(750)
.attrTween("d", tweenWith(hand))
;
line.exit().transition()
.ease("bounce")
.duration(750)
.attrTween("d", tweenWith(hand))
.remove()
;
}, 1000);
function tweenWith(f) {
return function arcTween(b) {
var i = d3.interpolate({value: b.previous}, b);
return function(t) {
return f(i(t));
};
};
}
</script>
</body>
</html>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment