Skip to content

Instantly share code, notes, and snippets.

@peterbsmyth
Created April 24, 2015 16:42
Show Gist options
  • Save peterbsmyth/005ed081c4b654ad04ce to your computer and use it in GitHub Desktop.
Save peterbsmyth/005ed081c4b654ad04ce to your computer and use it in GitHub Desktop.
Stacked Bar Chart with Mouseover Effect
[
{
"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"
}
}
]
<!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