A simple clock made with D3.
After I'd made it I got a bit caught up in trying to make it look like Dieter Ram's Braun clock face adding a whole bunch fo extra stuff...
It's still not quite there.
<!DOCTYPE html> | |
<meta charset="utf-8"> | |
<style> | |
body { | |
background: #fff; | |
} | |
svg{ | |
stroke: #000; | |
font-family: "HelveticaNeue-Light", "Helvetica Neue Light", "Helvetica Neue", Helvetica, Arial, "Lucida Grande", sans-serif; | |
} | |
#rim { | |
fill: none; | |
stroke: #999; | |
stroke-width: 3px; | |
} | |
.second-hand{ | |
stroke-width:3; | |
} | |
.minute-hand{ | |
stroke-width:8; | |
stroke-linecap:round; | |
} | |
.hour-hand{ | |
stroke-width:12; | |
stroke-linecap:round; | |
} | |
.hands-cover{ | |
stroke-width:3; | |
fill:#fff; | |
} | |
.second-tick{ | |
stroke-width:3; | |
fill:#000; | |
} | |
.hour-tick{ | |
stroke-width:8; //same as the miute hand | |
} | |
.second-label{ | |
font-size: 12px; | |
} | |
.hour-label{ | |
font-size: 24px; | |
} | |
</style> | |
<body> | |
<script src="http://d3js.org/d3.v3.min.js"></script> | |
<script> | |
var radians = 0.0174532925, | |
clockRadius = 200, | |
margin = 50, | |
width = (clockRadius+margin)*2, | |
height = (clockRadius+margin)*2, | |
hourHandLength = 2*clockRadius/3, | |
minuteHandLength = clockRadius, | |
secondHandLength = clockRadius-12, | |
secondHandBalance = 30, | |
secondTickStart = clockRadius; | |
secondTickLength = -10, | |
hourTickStart = clockRadius, | |
hourTickLength = -18 | |
secondLabelRadius = clockRadius + 16; | |
secondLabelYOffset = 5 | |
hourLabelRadius = clockRadius - 40 | |
hourLabelYOffset = 7; | |
var hourScale = d3.scale.linear() | |
.range([0,330]) | |
.domain([0,11]); | |
var minuteScale = secondScale = d3.scale.linear() | |
.range([0,354]) | |
.domain([0,59]); | |
var handData = [ | |
{ | |
type:'hour', | |
value:0, | |
length:-hourHandLength, | |
scale:hourScale | |
}, | |
{ | |
type:'minute', | |
value:0, | |
length:-minuteHandLength, | |
scale:minuteScale | |
}, | |
{ | |
type:'second', | |
value:0, | |
length:-secondHandLength, | |
scale:secondScale, | |
balance:secondHandBalance | |
} | |
]; | |
function drawClock(){ //create all the clock elements | |
updateData(); //draw them in the correct starting position | |
var svg = d3.select("body").append("svg") | |
.attr("width", width) | |
.attr("height", height); | |
var face = svg.append('g') | |
.attr('id','clock-face') | |
.attr('transform','translate(' + (clockRadius + margin) + ',' + (clockRadius + margin) + ')'); | |
//add marks for seconds | |
face.selectAll('.second-tick') | |
.data(d3.range(0,60)).enter() | |
.append('line') | |
.attr('class', 'second-tick') | |
.attr('x1',0) | |
.attr('x2',0) | |
.attr('y1',secondTickStart) | |
.attr('y2',secondTickStart + secondTickLength) | |
.attr('transform',function(d){ | |
return 'rotate(' + secondScale(d) + ')'; | |
}); | |
//and labels | |
face.selectAll('.second-label') | |
.data(d3.range(5,61,5)) | |
.enter() | |
.append('text') | |
.attr('class', 'second-label') | |
.attr('text-anchor','middle') | |
.attr('x',function(d){ | |
return secondLabelRadius*Math.sin(secondScale(d)*radians); | |
}) | |
.attr('y',function(d){ | |
return -secondLabelRadius*Math.cos(secondScale(d)*radians) + secondLabelYOffset; | |
}) | |
.text(function(d){ | |
return d; | |
}); | |
//... and hours | |
face.selectAll('.hour-tick') | |
.data(d3.range(0,12)).enter() | |
.append('line') | |
.attr('class', 'hour-tick') | |
.attr('x1',0) | |
.attr('x2',0) | |
.attr('y1',hourTickStart) | |
.attr('y2',hourTickStart + hourTickLength) | |
.attr('transform',function(d){ | |
return 'rotate(' + hourScale(d) + ')'; | |
}); | |
face.selectAll('.hour-label') | |
.data(d3.range(3,13,3)) | |
.enter() | |
.append('text') | |
.attr('class', 'hour-label') | |
.attr('text-anchor','middle') | |
.attr('x',function(d){ | |
return hourLabelRadius*Math.sin(hourScale(d)*radians); | |
}) | |
.attr('y',function(d){ | |
return -hourLabelRadius*Math.cos(hourScale(d)*radians) + hourLabelYOffset; | |
}) | |
.text(function(d){ | |
return d; | |
}); | |
var hands = face.append('g').attr('id','clock-hands'); | |
face.append('g').attr('id','face-overlay') | |
.append('circle').attr('class','hands-cover') | |
.attr('x',0) | |
.attr('y',0) | |
.attr('r',clockRadius/20); | |
hands.selectAll('line') | |
.data(handData) | |
.enter() | |
.append('line') | |
.attr('class', function(d){ | |
return d.type + '-hand'; | |
}) | |
.attr('x1',0) | |
.attr('y1',function(d){ | |
return d.balance ? d.balance : 0; | |
}) | |
.attr('x2',0) | |
.attr('y2',function(d){ | |
return d.length; | |
}) | |
.attr('transform',function(d){ | |
return 'rotate('+ d.scale(d.value) +')'; | |
}); | |
} | |
function moveHands(){ | |
d3.select('#clock-hands').selectAll('line') | |
.data(handData) | |
.transition() | |
.attr('transform',function(d){ | |
return 'rotate('+ d.scale(d.value) +')'; | |
}); | |
} | |
function updateData(){ | |
var t = new Date(); | |
handData[0].value = (t.getHours() % 12) + t.getMinutes()/60 ; | |
handData[1].value = t.getMinutes(); | |
handData[2].value = t.getSeconds(); | |
} | |
drawClock(); | |
setInterval(function(){ | |
updateData(); | |
moveHands(); | |
}, 1000); | |
d3.select(self.frameElement).style("height", height + "px"); | |
</script> |