Last active
August 29, 2015 14:11
-
-
Save kevinwarne/e46e8a510013cec22b2e to your computer and use it in GitHub Desktop.
D3 Analog/Digital Clock
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"> | |
<title>D3 Digital/Analog Clock</title> | |
<style> | |
.clock-outline { | |
stroke-width: 3px; | |
stroke: #666; | |
fill: #f8f8f8; | |
} | |
.clock-center circle{ | |
stroke-width: 3px; | |
stroke: #666; | |
/* fill: #666;*/ | |
} | |
rect.tick{ | |
fill:#666; | |
} | |
text{ | |
fill:#666; | |
font-size:12px; | |
} | |
.digital-minutes text,.digital-hours text{ | |
font-size:60px; | |
} | |
.digital-hours text{ | |
text-anchor:middle; | |
} | |
.analog-hours,.analog-minutes,.analog-seconds{ | |
stroke:#666; | |
} | |
.analog-hours{ | |
stroke-width:9; | |
stroke-linecap:round; | |
} | |
.analog-minutes{ | |
stroke-width:7; | |
stroke-linecap:round; | |
} | |
.analog-seconds{ | |
stroke-width:3; | |
} | |
body{ | |
font-family:"Lucida Sans Unicode"; | |
} | |
</style> | |
<body> | |
<script src="http://d3js.org/d3.v3.min.js"></script> | |
<script src="http://d3js.org/d3.superformula.v0.min.js"></script> | |
<script> | |
var body = d3.select('body') | |
var size = 400; | |
//make radio buttons | |
body.append('label') | |
.attr('for','analog') | |
.text('Analog'); | |
body.append('input') | |
.attr('type','radio') | |
.attr('name','clock-type') | |
.attr('value','analog') | |
.attr('id','analog') | |
.attr('checked','checked') | |
.html('Analog') | |
.on('change',radio_button_click); | |
body.append('br'); | |
body.append('label') | |
.attr('for','digital') | |
.text('Digital'); | |
body.append('input') | |
.attr('type','radio') | |
.attr('name','clock-type') | |
.attr('value','digital') | |
.attr('id','digital') | |
.html('Digital') | |
.on('change',radio_button_click); | |
body.append('br'); | |
var duration = 500; | |
var svg = body.append("svg") | |
.attr("width", 400) | |
.attr("height", 400); | |
var small = d3.superformula() | |
.type(function(d) { return d; }) | |
.size(size); | |
var sf = d3.superformula() | |
.type("circle") | |
.size(size * 150) | |
.segments(360); | |
var clockOutline = svg | |
.append("path") | |
.attr("class", "clock-outline") | |
.attr("transform", "translate(200,200)") | |
.attr("d", sf); | |
var digitalContent = svg | |
.append('g') | |
.attr('class','digital-content') | |
.attr('display','none') | |
.style('opacity',0) | |
.attr("transform", "translate(200,200)"); | |
var digitalMinutes = digitalContent | |
.append('g') | |
.attr("transform", "translate(40,22)") | |
.attr('class','digital-minutes') | |
.append('text') | |
var digitalHours = digitalContent | |
.append('g') | |
.attr("transform", "translate(-75,22)") | |
.attr('class','digital-hours') | |
.append('text') | |
var analogContent = svg | |
.append('g') | |
.attr('class','analog-content') | |
.attr("transform", "translate(200,200)"); | |
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 = [ | |
{'label':'hours', 'scale':hourScale, 'length': -100}, | |
{'label':'minutes', 'scale':minuteScale, 'length': -150}, | |
{'label':'seconds', 'scale':secondScale, 'length': -150} | |
] | |
var analogHands = analogContent.selectAll('.hand') | |
analogHands.data(handData).enter() | |
.append('g') | |
.attr('class',function(d){ return'hand analog-'+d.label;}) | |
.append('line') | |
.attr('x1',0) | |
.attr('y1',0) | |
.attr('x2',0) | |
.attr('y2',function(d){return d.length}) | |
var analogAxis = analogContent | |
.append('g') | |
.attr('class','analog-axis'); | |
var clockContent = svg | |
.append('g') | |
.attr('class','clock-content'); | |
var clockCenter = clockContent | |
.append('g') | |
.attr('class','clock-center') | |
.attr("transform", "translate(200,200)"); | |
var centerCircle = clockCenter.selectAll('circle').data([{r:8,y:0}]); | |
centerCircle.enter() | |
.append('circle') | |
.attr("transform", function(d){return "translate(0,"+d.y+")"}) | |
.attr('r',function(d){return d.r;}) | |
.style('fill', '#f8f8f8'); | |
analogAxis.selectAll("rect.tick") | |
.data(d3.range(60)) | |
.enter().append("svg:rect") | |
.attr("class", "tick") | |
.attr("x", -2) | |
.attr("y", -173) | |
.attr("width", 4) | |
.attr("height", function(d, i){return (i%5) ? 5 : 15;}) | |
.attr("transform", function(d, i){return "rotate("+(i*6)+")";}); | |
analogAxis.selectAll("text.label") | |
.data(d3.range(60)) | |
.enter().append("svg:text") | |
.attr("class", "label") | |
.attr("x", function(d, i){return (148)*Math.cos(i*0.1046-1.57)}) | |
.attr("y", function(d, i){return (150)*Math.sin(i*0.1046-1.57)}) | |
.attr("alignment-baseline", "middle") | |
.attr("text-anchor", "middle") | |
.attr("transform", function(d, i){return "translate(0,4.5)";}) | |
.text(function(d, i){return (i%5) ?'':d;}); | |
var timeUpdateInterval, | |
updateInterval = 1000; | |
toAnalog(); | |
function updateCenter(data, fill){ | |
centerCircle = clockCenter.selectAll('circle').data(data) | |
centerCircle.enter() | |
.append('circle') | |
.attr("r",8) | |
.style('fill',fill[0]); | |
centerCircle | |
.transition() | |
.duration(duration) | |
.attr("transform", function(d){return "translate(0,"+d.y+")"}) | |
.attr("r",function(d){return d.r;}) | |
.style('fill',fill[1]); | |
centerCircle.exit() | |
.transition() | |
.duration(duration) | |
.attr("transform", "translate(0,0)") | |
.attr("r", 8) | |
.style('fill',fill[1]) | |
.remove(); | |
} | |
function toDigital(){ | |
centerCircle.style('opacity',1) | |
analogContent | |
.transition() | |
.duration(duration/2) | |
.style('opacity',0) | |
.each('end',function(){d3.select(this).style('display','none');}); | |
digitalContent | |
.style('display','inline') | |
.transition() | |
.delay(duration/2) | |
.duration(duration/2) | |
.style('opacity',1); | |
updateCenter([{r:4,y:-20}, {r:4,y:20}], ['#f8f8f8','#666']); | |
//reset update interval and align with Date seconds | |
clearInterval(timeUpdateInterval); | |
updateDigitalTime(); | |
millisecondOffset = 1000 - new Date(Date.now()).getMilliseconds(); | |
setTimeout(function(){ | |
updateDigitalTime(); | |
timeUpdateInterval = setInterval(function(){ | |
updateDigitalTime(); | |
centerCircle.style('opacity',0) | |
setTimeout(function(){ | |
centerCircle.style('opacity',1) | |
},350) | |
},updateInterval); | |
}, millisecondOffset); | |
} | |
function toAnalog(){ | |
centerCircle.style('opacity',1) | |
analogContent | |
.style('display','inline') | |
.transition() | |
.delay(duration/2) | |
.duration(duration/2) | |
.style('opacity',1); | |
digitalContent | |
.transition() | |
.duration(duration/2) | |
.style('opacity',0) | |
.each('end',function(){d3.select(this).style('display','none');}); | |
updateCenter([{r:8,y:0}], ['#666','#f8f8f8']); | |
//reset update interval and align with Date seconds | |
clearInterval(timeUpdateInterval); | |
updateAnalogTime(); | |
millisecondOffset = 1000 - new Date().getMilliseconds(); | |
setTimeout(function(){ | |
updateAnalogTime(); | |
timeUpdateInterval = setInterval(updateAnalogTime,updateInterval); | |
}, millisecondOffset); | |
} | |
function updateDigitalTime(){ | |
var time = new Date(); | |
digitalHours.text(('0' + (time.getHours() % 12)).slice(-2)); | |
digitalMinutes.text(('0' + time.getMinutes()).slice(-2)); | |
} | |
function updateAnalogTime(){ | |
var time = new Date(); | |
handData[0].value = (time.getHours() % 12) + time.getMinutes()/60 ; | |
handData[1].value = time.getMinutes(); | |
handData[2].value = time.getSeconds(); | |
d3.selectAll('.hand').data(handData) | |
.transition() | |
.attr('transform',function(d){return 'rotate('+ d.scale(d.value) +')';}); | |
} | |
function radio_button_click(d){ | |
var checked_value = d3.select(this).attr('value'); | |
if(checked_value == 'digital'){ | |
d3.select(".clock-outline").transition().duration(duration).attr("d", sf.type('rectangle')); | |
toDigital(); | |
}else{ | |
d3.select(".clock-outline").transition().duration(duration).attr("d", sf.type('circle')); | |
toAnalog(); | |
} | |
} | |
</script> | |
</body> |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment