|
<!DOCTYPE html> |
|
<meta charset="utf-8"> |
|
<style> |
|
|
|
#clock { |
|
position: relative; |
|
background: #222; |
|
width: 960px; |
|
height: 800px; |
|
} |
|
|
|
#clock div { |
|
position: absolute; |
|
right: 4px; |
|
bottom: 4px; |
|
color: #ddd; |
|
font: 10px sans-serif; |
|
} |
|
|
|
#clock a { |
|
color: #fff; |
|
font-weight: bold; |
|
} |
|
|
|
text { |
|
font: 10px sans-serif; |
|
} |
|
|
|
</style> |
|
<div id="clock"> |
|
<div> |
|
Inspired by <a href="http://blog.pixelbreaker.com/polarclock/">pixelbreaker</a>. |
|
</div> |
|
</div> |
|
<script src="http://d3js.org/d3.v3.min.js"></script> |
|
<script> |
|
|
|
// Based on http://vis.stanford.edu/protovis/ex/clock.html |
|
// Based on http://blog.pixelbreaker.com/polarclock |
|
|
|
var w = 960, |
|
h = 800, |
|
r = Math.min(w, h) / 1.9, |
|
s = .09, |
|
fsec = d3.time.format("%S s"), |
|
fmin = d3.time.format("%M m"), |
|
fhou = d3.time.format("%H h"), |
|
fwee = d3.time.format("%a"), |
|
fdat = d3.time.format("%d d"), |
|
fmon = d3.time.format("%m M"), |
|
fyear = d3.time.format("%Y y"); |
|
|
|
var fill = d3.scale.linear() |
|
.range(["hsl(-180,50%,50%)", "hsl(180,50%,50%)"]) |
|
.interpolate(interpolateHsl); |
|
|
|
var arc = d3.svg.arc() |
|
.startAngle(0) |
|
.endAngle(function(d) { return d.value * 2 * Math.PI; }) |
|
.innerRadius(function(d) { return d.index * r; }) |
|
.outerRadius(function(d) { return (d.index + s) * r; }); |
|
|
|
var svg = d3.select("#clock").append("svg") |
|
.attr("width", w) |
|
.attr("height", h) |
|
.append("g") |
|
.attr("transform", "translate(" + w / 2 + "," + h / 2 + ")"); |
|
|
|
var g = svg.selectAll("g") |
|
.data(fields) |
|
.enter().append("g"); |
|
|
|
g.append("path") |
|
.style("fill", function(d) { return fill(d.value); }) |
|
.attr("d", arc); |
|
|
|
g.append("text") |
|
.attr("text-anchor", "middle") |
|
.attr("dy", "1em") |
|
.text(function(d) { return d.text; }); |
|
|
|
// Update arcs. |
|
d3.timer(function() { |
|
var g = svg.selectAll("g") |
|
.data(fields); |
|
|
|
g.select("path") |
|
.style("fill", function(d) { return fill(d.value); }) |
|
.attr("d", arc); |
|
|
|
g.select("text") |
|
.attr("dy", function(d) { return d.value < .5 ? "-.5em" : "1em"; }) |
|
.attr("transform", function(d) { |
|
return "rotate(" + 360 * d.value + ")" |
|
+ "translate(0," + -(d.index + s / 2) * r + ")" |
|
+ "rotate(" + (d.value < .5 ? -90 : 90) + ")" |
|
}) |
|
.text(function(d) { return d.text; }); |
|
}); |
|
|
|
d3.select(self.frameElement).style("height", h + "px"); |
|
|
|
// Generate the fields for the current date/time. |
|
function fields() { |
|
var launchDate = new Date('June 13, 2016 11:13:00'); |
|
var digDate = new Date('June 20, 2066 11:13:00'); |
|
var now = new Date; |
|
var d = new Date(digDate - now); |
|
|
|
function days() { |
|
return 32 - new Date(d.getYear(), d.getMonth(), 32).getDate(); |
|
} |
|
|
|
var second = (d.getSeconds() + d.getMilliseconds() / 1000) / 60, |
|
minute = (d.getMinutes() + second) / 60, |
|
hour = (d.getHours() + minute) / 24, |
|
weekday = (d.getDay() + hour) / 7, |
|
date = (d.getDate() - 1 + hour) / days(), |
|
month = (d.getMonth() + date) / 12, |
|
days = (d.getDay()) / 7, |
|
months = (d.getMonth()) / 12, |
|
years = ((d.getFullYear()) - 1970) / 50; |
|
|
|
return [ |
|
{value: second, index: .6, text: 'secs'}, |
|
{value: minute, index: .5, text: 'mins'}, |
|
{value: hour, index: .4, text: 'hours'}, |
|
// {value: weekday, index: .3, text: fwee(d)}, |
|
{value: date, index: .3, text: 'days'}, |
|
// {value: days, index: .3, text: fdat(d)}, |
|
// {value: month, index: .1, text: fmon(d)}, |
|
{value: months, index: .2, text: 'months'}, |
|
{value: years, index: .1, text: 'years'} |
|
]; |
|
} |
|
|
|
// To avoid shortest-path interpolation. |
|
function interpolateHsl(a, b) { |
|
var i = d3.interpolateString(a, b); |
|
return function(t) { |
|
return d3.hsl(i(t)); |
|
}; |
|
} |
|
|
|
</script> |