Skip to content

Instantly share code, notes, and snippets.

@velickym
Last active June 1, 2016 13:19
Show Gist options
  • Save velickym/15d8c222750d333eab42bbbd0717c7d9 to your computer and use it in GitHub Desktop.
Save velickym/15d8c222750d333eab42bbbd0717c7d9 to your computer and use it in GitHub Desktop.
Circular clock
.idea/
config.codekit
<!DOCTYPE html>
<html>
<head>
<title>Circular Clock</title>
<script src="//d3js.org/d3.v4.0.0-alpha.44.min.js"></script>
<style>
body {
text-align: center;
}
svg text {
font-size: 14px;
}
svg path {
stroke-width: 3px;
stroke: #fff;
}
#indicator {
fill: none;
stroke: black;
stroke-width: 4px;
}
</style>
<body>
<svg width="520" height="520"></svg>
<script type="text/javascript">
function textAngle(angle) {
return angle - 90;
}
function angle(d, offset) {
return (d.startAngle + d.endAngle) * 90 / Math.PI + offset;
}
var svg = d3.select("svg"),
width = +svg.attr("width"),
height = +svg.attr("height"),
radius = 280,
pi = Math.PI,
duration = 750;
var fields = [
{r1: 0.2 * radius, r2: 0.35 * radius, interval: d3.timeWeek, subinterval: d3.timeDay, format: d3.timeFormat("%a"), color: "#98DF8A" },
{r1: 0.35 * radius, r2: 0.45 * radius, interval: d3.timeMonth, subinterval: d3.timeDay, format: d3.timeFormat("%d"), color: "#FFBB78" },
{r1: 0.45 * radius, r2: 0.6 * radius, interval: d3.timeYear, subinterval: d3.timeMonth, format: d3.timeFormat("%b"), color: "#C5B0D5" },
{r1: 0.6 * radius, r2: 0.7 * radius, interval: d3.timeDay, subinterval: d3.timeHour, format: d3.timeFormat("%H"), color: "#cdddf2" },
{r1: 0.7 * radius, r2: 0.8 * radius, interval: d3.timeHour, subinterval: d3.timeMinute, format: d3.timeFormat("%M"), color: "#cdddf2" },
{r1: 0.8 * radius, r2: 0.9 * radius, interval: d3.timeMinute, subinterval: d3.timeSecond, format: d3.timeFormat("%S"), color: "#cdddf2" }
];
var pie = d3.pie().startAngle(pi / 2).endAngle(2 * pi + pi / 2).value(function() {
return 1;
}).sort(null);
var svgBody = svg.append("g")
.attr("transform", "translate(" + width / 2 + "," + height / 2 + ")");
var groups = svgBody.selectAll("g")
.data(fields)
.enter()
.append("g")
.attr("class", "field");
var fieldTicks = groups.selectAll(".field-tick").data(function(d) {
d.dateStart = d.interval.floor(new Date());
d.range = d.subinterval.range(d.dateStart, d.interval.offset(d.dateStart, 1));
d.arc = d3.arc().innerRadius(d.r1).outerRadius(d.r2);
d.slices = pie(d.range);
return d.range.map(function(x) {
return {
date: x,
field: d
};
});
}).enter().append("g").attr("class", "field-tick");
fieldTicks.append("path").attr("d", function(d, i) {
var field = this.parentNode.__data__.field;
return field.arc(field.slices[i]);
}).attr("fill", function() {
var field = this.parentNode.__data__.field;
return field.color;
});
fieldTicks.append("text")
.text(function(d) {
return d.field.format(d.date);
})
.attr("dy", ".35em")
.attr("text-anchor", "middle")
.attr("transform", function(d, i) {
var field = this.parentNode.__data__.field;
var slices = field.slices;
var coords = field.arc.centroid(slices[i]);
return "translate(" + coords + ")rotate(" + angle(slices[i], -90) + ")";
});
svg.append("rect")
.attr("id", "indicator")
.attr("width", radius * 0.7)
.attr("fill", "red")
.attr("height", 26)
.attr("x", width / 2 + radius * 0.2).attr("y", height / 2 - 13);
(function tick() {
var now = new Date,
then = new Date(+now + duration),
next = d3.timeSecond.offset(d3.timeSecond(then), 1),
delay = next - duration - now;
// Skip ahead a second if there’s not time for this transition.
if (delay < duration) {
delay += 1000;
then = next;
}
groups.transition().duration(duration)
.each(function(d, i) {
var index = d.subinterval.count(d.dateStart, then);
var offset = (d.slices[i].endAngle - d.slices[i].startAngle) / 2;
d.angle = - (2 * pi / d.range.length) * index - offset;
}).attr("transform", function(d) {
return "rotate(" + d.angle / pi * 180 + ")";
});
setTimeout(tick, delay);
})();
</script>
</body>
</html>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment