Skip to content

Instantly share code, notes, and snippets.

@mbostock
Last active October 30, 2022 16:09
  • Star 3 You must be signed in to star a gist
  • Fork 2 You must be signed in to fork a gist
Star You must be signed in to star a gist
Save mbostock/b89c89ec6b58435956a1 to your computer and use it in GitHub Desktop.
Polar Clock II
license: gpl-3.0
height: 960
<!DOCTYPE html>
<meta charset="utf-8">
<style>
body {
background: #222;
margin: auto;
width: 960px;
}
.tracks {
fill: none;
stroke: #000;
stroke-width: 1.5px;
}
.bodies path {
stroke: #000;
stroke-width: 1.5px;
}
.bodies .text-path {
stroke: none;
}
.bodies text {
fill: #000;
font: 500 16px "Helvetica Neue";
}
</style>
<svg width="960" height="960"></svg>
<script src="//d3js.org/d3.v4.0.0-alpha.21.min.js"></script>
<script>
var svg = d3.select("svg"),
width = +svg.attr("width"),
height = +svg.attr("height"),
radius = Math.min(width, height) / 1.9,
bodyRadius = radius / 23,
dotRadius = bodyRadius - 8;
var pi = Math.PI;
var fields = [
{radius: 0.2 * radius, format: d3.timeFormat("%B"), interval: d3.timeYear},
{radius: 0.3 * radius, format: formatDate, interval: d3.timeMonth},
{radius: 0.4 * radius, format: d3.timeFormat("%A"), interval: d3.timeWeek},
{radius: 0.6 * radius, format: d3.timeFormat("%-H hours"), interval: d3.timeDay},
{radius: 0.7 * radius, format: d3.timeFormat("%-M minutes"), interval: d3.timeHour},
{radius: 0.8 * radius, format: d3.timeFormat("%-S seconds"), interval: d3.timeMinute}
];
var color = d3.scaleRainbow()
.domain([0, 360]);
var arcBody = d3.arc()
.startAngle(function(d) { return bodyRadius / d.radius; })
.endAngle(function(d) { return -pi - bodyRadius / d.radius; })
.innerRadius(function(d) { return d.radius - bodyRadius; })
.outerRadius(function(d) { return d.radius + bodyRadius; })
.cornerRadius(bodyRadius);
var arcTextPath = d3.arc()
.startAngle(function(d) { return -bodyRadius / d.radius; })
.endAngle(-pi)
.innerRadius(function(d) { return d.radius; })
.outerRadius(function(d) { return d.radius; });
var g = svg.append("g")
.attr("transform", "translate(" + width / 2 + "," + height / 2 + ")");
g.append("g")
.attr("class", "tracks")
.selectAll("circle")
.data(fields)
.enter().append("circle")
.attr("r", function(d) { return d.radius; });
var body = g.append("g")
.attr("class", "bodies")
.selectAll("g")
.data(fields)
.enter().append("g");
body.append("path")
.attr("d", function(d) {
return arcBody(d)
+ "M0," + (dotRadius - d.radius)
+ "a" + dotRadius + "," + dotRadius + " 0 0,1 0," + -dotRadius * 2
+ "a" + dotRadius + "," + dotRadius + " 0 0,1 0," + dotRadius * 2;
});
body.append("path")
.attr("class", "text-path")
.attr("id", function(d, i) { return "body-text-path-" + i; })
.attr("d", arcTextPath);
var bodyText = body.append("text")
.attr("dy", ".35em")
.append("textPath")
.attr("xlink:href", function(d, i) { return "#body-text-path-" + i; });
tick();
d3.timer(tick);
function tick() {
var now = Date.now();
fields.forEach(function(d) {
var start = d.interval(now),
end = d.interval.offset(start, 1);
d.angle = Math.round((now - start) / (end - start) * 360 * 100) / 100;
});
body
.style("fill", function(d) { return color(d.angle); })
.attr("transform", function(d) { return "rotate(" + d.angle + ")"; });
bodyText
.attr("startOffset", function(d, i) { return d.angle <= 90 || d.angle > 270 ? "100%" : "0%"; })
.attr("text-anchor", function(d, i) { return d.angle <= 90 || d.angle > 270 ? "end" : "start"; })
.text(function(d) { return d.format(now); });
}
function formatDate(d) {
d = new Date(d).getDate();
switch (10 <= d && d <= 19 ? 10 : d % 10) {
case 1: d += "st"; break;
case 2: d += "nd"; break;
case 3: d += "rd"; break;
default: d += "th"; break;
}
return d;
}
</script>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment