Another variation of a polar clock, inspired by a Flash screensaver by pixelbreaker.
-
-
Save dysbulic/6c1ad59c6312f3d79bef to your computer and use it in GitHub Desktop.
Polar Clock III
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
license: gpl-3.0 | |
height: 100% |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// ♑ 1/20 | |
// ♒ 2/16 | |
// ♓ 3/11 | |
// ♈ 4/18 | |
// ♉ 5/13 | |
// ♊ 6/21 | |
// ♋ 7/20 | |
// ♌ 8/10 | |
// ♍ 9/16 | |
// ♎ 10/30 | |
// ♏ 11/23 | |
// ⛎ 11/29 | |
// ♐ 12/18 |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<!DOCTYPE html> | |
<meta charset="utf-8"> | |
<style> | |
body { | |
background: #222; | |
margin: auto; | |
width: 960px; | |
} | |
.field-track, | |
.field-arm { | |
fill: none; | |
stroke: #000; | |
stroke-width: 1.5px; | |
} | |
.field-tick { | |
transition: opacity 750ms linear; | |
} | |
.field-tick:not(.field-tick--active) circle, | |
.field-tick:not(.field-tick--active):first-of-type text { | |
fill: #222 !important; | |
} | |
.field-tick:not(.field-tick--active):first-of-type circle { | |
fill: #000 !important; | |
} | |
.field-tick--disabled { | |
opacity: 0; | |
} | |
.field-tick circle, | |
.field-tick text { | |
transition: fill 250ms linear; | |
transition-delay: 400ms; | |
} | |
.field-tick text { | |
font: 700 14px "Helvetica Neue"; | |
text-anchor: middle; | |
} | |
</style> | |
<svg width="100%" height="100%"></svg> | |
<script src="//d3js.org/d3.v4.0.0-alpha.24.min.js"></script> | |
<script> | |
var svg = d3.select("svg"), | |
width = +svg.attr("width"), | |
height = +svg.attr("height"), | |
radius = Math.min(width, height) / 1.9, | |
armRadius = radius / 22, | |
dotRadius = armRadius - 6; | |
var duration = 750, | |
now = new Date(Date.now() + 2 * duration); | |
var pi = Math.PI, | |
tau = pi * 2; | |
var fields = [ | |
{radius: 0.2 * radius, interval: d3.timeYear, subinterval: d3.timeMonth, format: d3.timeFormat("%b")}, | |
{radius: 0.3 * radius, interval: d3.timeMonth, subinterval: d3.timeDay, format: d3.timeFormat("%d")}, | |
{radius: 0.4 * radius, interval: d3.timeWeek, subinterval: d3.timeDay, format: d3.timeFormat("%a")}, | |
{radius: 0.6 * radius, interval: d3.timeDay, subinterval: d3.timeHour, format: d3.timeFormat("%H")}, | |
{radius: 0.7 * radius, interval: d3.timeHour, subinterval: d3.timeMinute, format: d3.timeFormat("%M")}, | |
{radius: 0.8 * radius, interval: d3.timeMinute, subinterval: d3.timeSecond, format: d3.timeFormat("%S")} | |
]; | |
var color = d3.scaleRainbow() | |
.domain([0, tau]); | |
var arcArm = d3.arc() | |
.startAngle(function(d) { return armRadius / d.radius; }) | |
.endAngle(function(d) { return -pi - armRadius / d.radius; }) | |
.innerRadius(function(d) { return d.radius - armRadius; }) | |
.outerRadius(function(d) { return d.radius + armRadius; }) | |
.cornerRadius(armRadius); | |
var field = svg.append("g") | |
.attr("transform", "translate(" + width / 2 + "," + height / 2 + ")") | |
.selectAll(".field") | |
.data(fields) | |
.enter().append("g") | |
.attr("class", "field"); | |
field.append("circle") | |
.attr("class", "field-track") | |
.attr("r", function(d) { return d.radius; }); | |
var fieldTick = field.selectAll(".field-tick") | |
.data(function(d) { | |
var date = d.interval(new Date(2000, 0, 1)); | |
d.range = d.subinterval.range(date, d.interval.offset(date, 1)); | |
return d.range.map(function(t) { return {time: t, field: d}; }); | |
}) | |
.enter().append("g") | |
.attr("class", "field-tick") | |
.attr("transform", function(d, i) { | |
var angle = i / d.field.range.length * tau - pi / 2; | |
return "translate(" + Math.cos(angle) * d.field.radius + "," + Math.sin(angle) * d.field.radius + ")"; | |
}); | |
fieldTick.append("circle") | |
.attr("r", dotRadius - 3) | |
.style("fill", function(d, i) { return color(i / d.field.range.length * tau); }); | |
fieldTick.append("text") | |
.attr("dy", "0.35em") | |
.text(function(d) { return d.field.format(d.time).slice(0, 2); }); | |
var fieldArm = field.append("path") | |
.attr("class", "field-arm") | |
.attr("transform", "rotate(0)") | |
.attr("d", function(d) { | |
return arcArm(d) | |
+ "M0," + (dotRadius - d.radius) | |
+ "a" + dotRadius + "," + dotRadius + " 0 0,1 0," + -dotRadius * 2 | |
+ "a" + dotRadius + "," + dotRadius + " 0 0,1 0," + dotRadius * 2; | |
}); | |
(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; | |
fieldArm.transition() | |
.duration(duration) | |
.each(function(d) { | |
var start = d.interval(then); | |
d.activeLength = d.subinterval.count(start, d.interval.offset(start, 1)); | |
d.activeIndex = d.subinterval.count(start, then); | |
d.angle = d.activeIndex / d.range.length * tau; | |
}) | |
.attr("transform", function(d) { return "rotate(" + d.angle / pi * 180 + ")"; }) | |
.style("fill", function(d) { return color(d.angle); }); | |
fieldTick | |
.classed("field-tick--disabled", function(d, i) { return i >= d.field.activeLength; }) | |
.classed("field-tick--active", function(d, i) { return i === d.field.activeIndex; }); | |
setTimeout(tick, delay); | |
})(); | |
</script> |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment