Created
April 24, 2015 16:42
-
-
Save peterbsmyth/005ed081c4b654ad04ce to your computer and use it in GitHub Desktop.
Stacked Bar Chart with Mouseover Effect
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
[ | |
{ | |
"date": "2015-04-18T04:00:00.000Z", | |
"events": 11, | |
"duration": { | |
"zazen": "1990-09-13T04:00:00.000Z", | |
"work": "1990-09-13T06:45:00.000Z", | |
"social": "1990-09-13T08:00:00.000Z", | |
"learn": "1990-09-13T04:00:00.000Z", | |
"bike": "1990-09-13T05:30:00.000Z", | |
"eatwell": "1990-09-13T06:30:00.000Z", | |
"slack": "1990-09-13T08:00:00.000Z" | |
} | |
}, | |
{ | |
"date": "2015-04-19T04:00:00.000Z", | |
"events": 4, | |
"duration": { | |
"zazen": "1990-09-13T04:00:00.000Z", | |
"work": "1990-09-13T04:00:00.000Z", | |
"social": "1990-09-13T09:15:00.000Z", | |
"learn": "1990-09-13T04:00:00.000Z", | |
"bike": "1990-09-13T04:00:00.000Z", | |
"eatwell": "1990-09-13T05:30:00.000Z", | |
"slack": "1990-09-13T12:15:00.000Z" | |
} | |
}, | |
{ | |
"date": "2015-04-20T04:00:00.000Z", | |
"events": 14, | |
"duration": { | |
"zazen": "1990-09-13T04:10:00.000Z", | |
"work": "1990-09-13T12:00:00.000Z", | |
"social": "1990-09-13T05:05:00.000Z", | |
"learn": "1990-09-13T04:00:00.000Z", | |
"bike": "1990-09-13T04:00:00.000Z", | |
"eatwell": "1990-09-13T06:45:00.000Z", | |
"slack": "1990-09-13T07:30:00.000Z" | |
} | |
}, | |
{ | |
"date": "2015-04-21T04:00:00.000Z", | |
"events": 15, | |
"duration": { | |
"zazen": "1990-09-13T04:20:00.000Z", | |
"work": "1990-09-13T12:05:00.000Z", | |
"social": "1990-09-13T05:15:00.000Z", | |
"learn": "1990-09-13T05:10:00.000Z", | |
"bike": "1990-09-13T05:35:00.000Z", | |
"eatwell": "1990-09-13T06:30:00.000Z", | |
"slack": "1990-09-13T04:25:00.000Z" | |
} | |
}, | |
{ | |
"date": "2015-04-22T04:00:00.000Z", | |
"events": 16, | |
"duration": { | |
"zazen": "1990-09-13T04:30:00.000Z", | |
"work": "1990-09-13T12:20:00.000Z", | |
"social": "1990-09-13T05:55:00.000Z", | |
"learn": "1990-09-13T06:30:00.000Z", | |
"bike": "1990-09-13T04:00:00.000Z", | |
"eatwell": "1990-09-13T05:15:00.000Z", | |
"slack": "1990-09-13T05:00:00.000Z" | |
} | |
}, | |
{ | |
"date": "2015-04-23T04:00:00.000Z", | |
"events": 17, | |
"duration": { | |
"zazen": "1990-09-13T04:20:00.000Z", | |
"work": "1990-09-13T12:00:00.000Z", | |
"social": "1990-09-13T05:35:00.000Z", | |
"learn": "1990-09-13T05:20:00.000Z", | |
"bike": "1990-09-13T06:00:00.000Z", | |
"eatwell": "1990-09-13T05:40:00.000Z", | |
"slack": "1990-09-13T04:45:00.000Z" | |
} | |
}, | |
{ | |
"date": "2015-04-24T04:00:00.000Z", | |
"events": 4, | |
"duration": { | |
"zazen": "1990-09-13T04:15:00.000Z", | |
"work": "1990-09-13T06:20:00.000Z", | |
"social": "1990-09-13T04:00:00.000Z", | |
"learn": "1990-09-13T04:00:00.000Z", | |
"bike": "1990-09-13T04:00:00.000Z", | |
"eatwell": "1990-09-13T04:35:00.000Z", | |
"slack": "1990-09-13T04:05:00.000Z" | |
} | |
} | |
] |
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> | |
<html> | |
<head> | |
<meta charset="utf-8"> <!--set charset to utf-8--> | |
<meta name="viewport" content="width=device-width, initial-scale=1.0"> <!--enable mobile views--> | |
<!-- CSS --> | |
<link rel="stylesheet" type="text/css" href="css/normalize.css"> <!--reset css for cross-browser compatibility--> | |
<link rel="stylesheet" type="text/css" href="css/bootstrap.css"> | |
<link rel="stylesheet" type="text/css" href="css/agency.css"> | |
<!-- <link rel="stylesheet" type="text/css" href="css/bootstrap-theme.css"> --> | |
<link rel="stylesheet" type="text/css" href="css/main.css"> | |
<link rel="stylesheet" type="text/css" href="css/responsive.css"> | |
<!-- JS --> | |
<script type="text/javascript" src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.3/jquery.min.js"></script> | |
<script src="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.4/js/bootstrap.min.js"></script> | |
<script src="https://cdnjs.cloudflare.com/ajax/libs/d3/3.5.5/d3.min.js"></script> | |
<title>D3 Stacked Bar Chart</title> | |
<style> | |
.axis text { | |
font: 10px sans-serif; | |
} | |
.axis path, | |
.axis line { | |
fill: none; | |
stroke: #000; | |
shape-rendering: crispEdges; | |
} | |
</style> | |
</head> | |
<body> | |
<div class="container"> | |
<div class="row"> | |
<div class="chart-container"> | |
<svg class="chart"></svg> | |
</div> | |
</div> | |
</div> | |
<script type="text/javascript"> | |
//Helpers to convert Date Objects | |
Date.prototype.toHoursDotMinutes = function(){ | |
var minutes = this.getMinutes()/60; | |
var hours = this.getHours(); | |
return hours+minutes; | |
} | |
Date.prototype.toChartDurationFormat = function(){ | |
var hours = this.getHours(); | |
var minutes = this.getMinutes(); | |
return hours + " Hrs" + " " + minutes + " Mins"; | |
} | |
//Conventional D3 Margin | |
var margin = {top: 20, right: 30, bottom: 30, left: 40}, | |
width = 900 - margin.left - margin.right, | |
height = 500 - margin.top - margin.bottom; | |
d3.json("data.json",function(dates){ | |
//Convert each duration to hours.minutes format | |
dates.forEach(function(item){ | |
item.date = new Date(item.date); | |
for(var key in item.duration){ | |
item.duration[key+"hDM"] = new Date(item.duration[key]).toHoursDotMinutes(); | |
} | |
}); | |
//Set minimum and maximum date for input domain | |
var minDate = dates[0].date; | |
var maxDate = dates[dates.length-1].date; | |
var xScale = d3.time.scale.utc() | |
.domain([minDate,d3.time.day.utc.offset(maxDate,1)]) | |
.range([0,width]); | |
var yScale = d3.scale.linear() | |
.domain([0,24]) | |
.range([0,height]); | |
var xAxis = d3.svg.axis() | |
.orient("top") | |
.ticks(d3.time.days.utc,1) //I don't understand how this works. *Magically* displays domain days | |
.tickFormat(d3.time.format('%a, %m/%d')) | |
.scale(xScale); | |
var yAxis = d3.svg.axis() | |
.orient("left") | |
.ticks(24) | |
.scale(yScale); | |
var barWidth = width / dates.length; | |
var color = d3.scale.ordinal() | |
.range(["#98abc5", "#8a89a6", "#7b6888", "#6b486b", "#a05d56", "#d0743c", "#ff8c00"]); //http://bl.ocks.org/mbostock/3886208 | |
//set domain of color to be duration names that do not contain hDM | |
// key.indexOf("hDM") returns -1 if "hDM" is not in string | |
color.domain(d3.keys(dates[0].duration).filter(function(key) { return key.indexOf("hDM") === -1; })); | |
//calculate y positions for data and include duration in JavaScript Date Object to append later | |
dates.forEach(function(d) { | |
var y0 = 0; | |
d.duration.pillars = color.domain().map(function(name) { return {name: name, y0: y0, y1: y0 += +d.duration[name+ "hDM"], duration: d.duration[name]}; }); | |
d.total = d.duration.pillars[d.duration.pillars.length - 1].y1; | |
}); | |
//Boilerplate chart append | |
var chart = d3.select(".chart") | |
.attr("width", width + margin.left + margin.right) | |
.attr("height", height + margin.top + margin.bottom) | |
.append("g") | |
.attr("transform", "translate(" + margin.left + "," + margin.top + ")"); | |
//Append x axis | |
chart.append("g") | |
.attr("class", "x axis") // give it a class so it can be used to select only xaxis labels below | |
.attr("transform", "translate(0,-1)") | |
.call(xAxis); | |
//Append y axis | |
chart.append("g") | |
.attr("class", "y axis") // give it a class so it can be used to select only yaxis labels below | |
.attr("transform", "translate(-1,0)") | |
.call(yAxis) | |
.append("text") | |
.attr("transform", "rotate(-90)") | |
.attr("y", 6) | |
.attr("x",-425) | |
.attr("dy", ".71em") | |
.style("text-anchor", "end") | |
.text("Hours"); | |
var dateBar = chart.selectAll(".dateBar") | |
.data(dates) | |
.enter().append("g") | |
.attr("class", "g") | |
.attr("transform", function(d) { return "translate(" + xScale(d.date) + ",0)"; }); | |
dateBar.selectAll("rect") | |
.data(function(d) { return d.duration.pillars; }) | |
.enter().append("rect") | |
.attr("width", barWidth-1) | |
.attr("y", function(d) { return yScale(d.y0); }) | |
.attr("height", function(d) { | |
return yScale(d.y1) - yScale(d.y0); }) | |
.style("fill", function(d) { return color(d.name); }) | |
.on('mouseover', function(d){ | |
d3.select(this) | |
.style("fill", "orange"); | |
chart.append("text") | |
.attr("id", "tooltip") | |
.attr("x", 450) | |
.attr("y", height) | |
.attr("text-anchor", "middle") | |
.attr("font-family", "sans-serif") | |
.attr("font-size", "15px") | |
.attr("font-weight", "bold") | |
.attr("fill", "black") | |
.text("Pillar: " + d.name + " Duration: " + new Date (d.duration).toChartDurationFormat()); | |
}) | |
.on("mouseout", function(d) { | |
d3.select(this) | |
.transition() | |
.duration(250) | |
.style("fill", function(d) { return color(d.name); }); | |
d3.select("#tooltip").remove(); | |
}); | |
var legend = chart.selectAll(".legend") | |
.data(color.domain()) | |
.enter().append("g") | |
.attr("class", "legend") | |
.attr("transform", function(d, i) { return "translate(0," + i * 20 + ")"; }); | |
legend.append("rect") | |
.attr("x", width - 18) | |
.attr("y", 320) | |
.attr("width", 18) | |
.attr("height", 18) | |
.style("fill", color); | |
legend.append("text") | |
.attr("x", width - 24) | |
.attr("y", 329) | |
.attr("dy", ".35em") | |
.style("text-anchor", "end") | |
.text(function(d) { return d; }); | |
}); | |
</script> | |
</body> | |
</html> |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment