Built with blockbuilder.org
Experiment on how to display all hours of a (some) weeks in a circular manner using d3.
Built with blockbuilder.org
Experiment on how to display all hours of a (some) weeks in a circular manner using d3.
<!DOCTYPE html> | |
<head> | |
<meta charset="utf-8"> | |
<script src="https://cdnjs.cloudflare.com/ajax/libs/d3/3.5.5/d3.min.js"></script> | |
<script src="https://cdnjs.cloudflare.com/ajax/libs/d3/3.5.5/d3.min.js"></script> | |
<style> | |
body { margin:0;position:fixed;top:0;right:0;bottom:0;left:0; } | |
svg { width:100%; height: 100% } | |
</style> | |
</head> | |
<body> | |
<svg></svg> | |
<script> | |
// magic numbers | |
var t = 2 * Math.PI; | |
var depth = 3; | |
var arcHeight = 50; | |
var innerRadius = 50; | |
var hours = 24 * 7; | |
var intensityThreshold = 0.5; | |
// arc generators | |
var arc = d3.svg.arc() | |
.innerRadius(function(d,i) { return innerRadius + (d.depth+1) * arcHeight; }) | |
.outerRadius(function(d,i) { return innerRadius + d.depth * arcHeight + arcHeight*2; }) | |
.startAngle(function(d, i){ return d.start;}) | |
.endAngle(function(d, i){ return d.end;}); | |
var overlayArc = d3.svg.arc() | |
.innerRadius(arcHeight + innerRadius) | |
.outerRadius(innerRadius + depth * arcHeight + arcHeight) | |
.startAngle(function(d){ return d.start;}) | |
.endAngle(function(d){ return d.end;}); | |
function postion(i,offset) { | |
offset = offset || 0; | |
return (i*24)*((2*Math.PI) / hours) + (offset)*((2*Math.PI) / hours) | |
} | |
var dayArc = d3.svg.arc() | |
.innerRadius( innerRadius + arcHeight) | |
.outerRadius(innerRadius + (depth+1) * arcHeight) | |
.startAngle(function(d,i){ return postion(i) ;}) | |
.endAngle(function(d,i){ return postion(i,24);}); | |
var labelArc = d3.svg.arc() | |
.innerRadius( innerRadius + (depth+1) * arcHeight + 10) | |
.outerRadius(innerRadius + (depth+2) * arcHeight) | |
.startAngle(function(d,i){ return postion(i) ;}) | |
.endAngle(function(d,i){ return postion(i,24);}) | |
.padAngle(.01); | |
// get svg object | |
var svg = d3.select("svg") | |
.append("g") | |
.attr("transform", "translate(310, 310)"); | |
// data | |
var data = d3.range(0,hours*depth); | |
data = data.map(function(d) { | |
var intensity = Math.random(); | |
return { | |
hoursIndex: d % hours, | |
hour: d%24, | |
start: d*((2*Math.PI) / hours), | |
end: d*((2*Math.PI) / hours) + ((2*Math.PI) / hours), | |
color: intensity < intensityThreshold ? 0 : intensity, | |
depth: Math.floor(d/hours) | |
} | |
}); | |
var days = ["Monday","Tuesday", "Wednesday","Thursday","Friday","Saturday","Sunday"]; | |
var dataOverlay = d3.range(0,hours) | |
dataOverlay = dataOverlay.map(function(d) { | |
return { | |
start: d*((2*Math.PI) / hours), | |
end: d*((2*Math.PI) / hours) + ((2*Math.PI) / hours), | |
data: d | |
} | |
}); | |
// Scales | |
var colorScaleDay = d3.scale.linear() | |
.domain([0, 1]) | |
.interpolate(d3.interpolateRgb) | |
.range(["#ffffff", "#ffba19"]); | |
var colorScaleNight = d3.scale.linear() | |
.domain([0, 1]) | |
.interpolate(d3.interpolateRgb) | |
.range(["#ffffff", "#2d73fe"]); | |
var opacityScale = d3.scale.pow().exponent(1.2) | |
.domain([0, 1]) | |
.range([0,1]); | |
// render viz | |
var select = svg.selectAll(".fore").data(data).enter(); | |
select.append("path") | |
.classed("fore", true) | |
.style({ | |
"fill-opacity": function(d) { return opacityScale(d.color); }, | |
"stroke": "black", | |
"stroke-width": 0.05, | |
"fill": function(d) { return (5 >= d.hour) || (18 <= d.hour) ? colorScaleNight(d.color) : colorScaleDay(d.color); } | |
}) | |
.attr("d", arc) | |
.transition(); | |
var opacity = 0; | |
svg.selectAll(".overlay").data(dataOverlay).enter() | |
.append("path") | |
.classed("overlay", true) | |
.style({ | |
"stroke": "black", | |
"stroke-opacity": opacity, | |
"stroke-width": 2, | |
"fill": "white", | |
"fill-opacity": 0 | |
}) | |
.attr("d", overlayArc) | |
.on('mouseover', function(d) { | |
d3.select(this).style("stroke-opacity", 1); | |
var dataindex = d3.select(this).data()[0].data; | |
var object = data.filter(function(d) { return d.hoursIndex == dataindex; }); | |
var result = object.map(function(d) { return d.color; }); | |
/*svg.select(".txt").data(result).enter() | |
.append("text").classed("txt",true) | |
.text(function(d) {return d[0];});*/ | |
}) | |
.on('mouseout', function(d) { | |
d3.select(this).style("stroke-opacity", opacity); | |
}); | |
// labeling | |
svg.append("circle") | |
.attr({ | |
cx: 0, | |
cy: 0, | |
r: innerRadius + arcHeight - 5 | |
}) | |
.style({ | |
"stroke-width": "1", | |
"stroke": "#606060", | |
"fill": "white" | |
}); | |
svg.append("text").text(depth + " weeks (in hours)") | |
.attr({ | |
x: 0, | |
y: 3 | |
}) | |
.style({ | |
"text-anchor": "middle", | |
"font-weight": "bold" | |
}); | |
// day label text on arch | |
svg.selectAll(".daySlice") | |
.data(days) | |
.enter().append("path") | |
.classed("daySlice",true) | |
.attr("d", dayArc) | |
.style("fill", "none") | |
.style("stroke", "#494949") | |
.style("stroke-width", 2); | |
svg.selectAll(".donutArcSlices") | |
.data(days) | |
.enter().append("path") | |
.classed("donutArcSlices",true) | |
.attr("id", function(d,i) { return "monthArc_"+i; }) | |
.attr("d", labelArc) | |
.style("fill", "none") | |
.style("stroke", "black") | |
.each(function(d,i) { | |
//A regular expression that captures all in between the start of a string (denoted by ^) | |
//and the first capital letter L | |
var firstArcSection = /(^.+?)L/; | |
//The [1] gives back the expression between the () (thus not the L as well) | |
//which is exactly the arc statement | |
var newArc = firstArcSection.exec( d3.select(this).attr("d") )[1]; | |
//Replace all the comma's so that IE can handle it -_- | |
//The g after the / is a modifier that "find all matches rather than stopping after the first match" | |
newArc = newArc.replace(/,/g , " "); | |
//Create a new invisible arc that the text can flow along | |
svg.append("path") | |
.attr("class", "hiddenDonutArcs") | |
.attr("id", "donutArc"+i) | |
.attr("d", newArc) | |
.style("fill", "none"); | |
}); | |
svg.selectAll(".donutText") | |
.data(days) | |
.enter().append("text") | |
.attr("class", "donutText") | |
.attr("dy", 17) | |
.append("textPath") | |
.attr("startOffset","50%") | |
.style("text-anchor","middle") | |
.attr("xlink:href",function(d,i){return "#donutArc"+i;}) | |
.text(function(d){return d;}); | |
</script> | |
</body> |