Skip to content

Instantly share code, notes, and snippets.

@BMPMS
Created November 24, 2016 11:14
Show Gist options
  • Save BMPMS/dc7def5f5b8edbd0f4aa6908dc8f59a4 to your computer and use it in GitHub Desktop.
Save BMPMS/dc7def5f5b8edbd0f4aa6908dc8f59a4 to your computer and use it in GitHub Desktop.
Waterfall
/*general */
text {
font: 10px sans-serif;
}
/*tooltip */
.line {
fill: none;
stroke: steelblue;
stroke-width: 1.5px;
}
.tooltip, .area_tooltip {
visibility: hidden;
position: absolute;
text-align: left;
border: 1px solid grey;
padding: 5px;
font: 10px sans-serif;
background: white;
border-radius: 4px;
pointer-events: none;
width: 150px;
height: 25px;
}
#waterfall {
width: 70px;
height: 50px;
}
/*X and Y axis */
.axis text {
font-family: sans-serif;
font-size: 10px;
}
.axis path,
.axis line {
fill: none;
stroke: #000;
shape-rendering: crispEdges;
}
.tick line{
visibility:hidden;
}
.tick {
text-align: left;
}
/* Z axis - lines inbetween*/
.zaxis line, .zaxis path {
fill: none;
stroke: lightgrey;
shape-rendering: crispEdges;
}
.zaxis text { display: none; }
.area {
stroke-width: 0;
opacity: 0.2;
}
.area.selected {
opacity: 0.6;
}
<!DOCTYPE html>
<html>
<link rel="stylesheet" type="text/css" href="boom_style.css" />
<head>
<meta charset="utf-8">
<title>Test Boomslang</title>
</head>
<body>
<div id='waterfall'></div>
</body>
<script src="http://d3js.org/d3.v4.js"></script>
<script src="waterfall.js"></script>
</html>
d3.json("waterfall.json", function(error, root) {
if (error) throw error;
var my_data = [],
locations = [];
//format the data
my_data = get_data(root)
for (var m in my_data) {
if (locations.indexOf(my_data[m].label) == -1) {
//build unique array of locations
locations.push(my_data[m].label);
}
}
//set responsive width, height and margins
var width = window.innerWidth || document.documentElement.clientWidth || document.body.clientWidth;
var height = window.innerHeight || document.documentElement.clientHeight || document.body.clientHeight;
var margin = {top: 20, right: 40, bottom: 70, left: 100},
width = width - margin.left - margin.right,
height = height - margin.top - margin.bottom,
padding = 100;
//draw svg
var svg = d3.select("#waterfall").append("svg")
.attr("width", width + margin.left + margin.right)
.attr("height", height + margin.top + margin.bottom)
.append("g")
.attr("transform",
"translate(" + margin.left + "," + margin.top + ")");
//define scales (x,y and color)
var x = d3.scaleLinear().domain([0,100]).rangeRound([0, width]);
var y = d3.scaleBand().domain(locations).rangeRound([0, height]);
var color = d3.scaleOrdinal(d3.schemeCategory10);
//define and append axes
svg.append("g")
.attr("class", "x axis")
.attr("transform", "translate(0," + height + ")")
.call(d3.axisBottom(x).tickSizeOuter(0).tickFormat(function(d) { return d + "%"; }));
svg.append("g")
.attr("class", "y axis")
.call(d3.axisLeft(y).tickSizeOuter(0));
//define tooltip
var tooltip = d3.select("body")
.append("div")
.attr("class", "tooltip")
.attr("id","waterfall")
.html("");
for (var l in locations){
//for every location, draw a divider line (z axis)
//change the height for each location and append axis
zheight = height - y(locations[l]) - y.bandwidth()
svg.append("g")
.attr("class","zaxis")
.attr("transform","translate(0," + zheight + ")")
.call(d3.axisBottom().scale(x).tickSizeOuter(0))
}
function gradient(colour,id,x1,x2,off1,off2,op1,op2){
//gradient function.
//defines the gradient
svg.append("defs")
.append("linearGradient")
.attr("id",id)
.attr("x1", x1).attr("y1", "0%")
.attr("x2", x2).attr("y2", "0%");
idtag = '#'+id
//defines the start
d3.select(idtag)
.append("stop")
.attr("stop-color", colour)
.attr("class","begin")
.attr("offset", off1)
.attr("stop-opacity", op1);
//and the finish
d3.select(idtag)
.append("stop")
.attr("class","end")
.attr("stop-color", colour)
.attr("offset", off2)
.attr("stop-opacity", op2);
}
//define bar height
var barheight = 44;
//draw bars (gradient filled rectangles in between two circles)
var bars = svg.selectAll(".bar")
.data(my_data)
.enter().append("rect")
.attr("class", "bar")
.style("fill",function(d) {
//set the gradient fill
//dependent on whether values have gone up or down
if(d.end_v>d.start_v){
gradient(color(d.label),'grad'+ d.id,"0%","100%","0%","100%",0.4,1)
}else{
gradient(color(d.label),'grad'+ d.id,"100%","0%","0%","100%",0.4,1)
}return "url(#grad" + d.id +")";
})
.attr("x", function(d) { return Math.min(x(d.start_v),x(d.end_v)); })
.attr("height", barheight)
.attr("y", function(d) { return y(d.label) + (y.bandwidth()/2) - barheight/2})
.attr("width", function(d) { return Math.abs(x(d.start_v)-x(d.end_v)); })
.on("mouseover", function(d){
debugger;
//defines the tooltip
t_text = d.label + "<br>" + d.start_t + ":" + d.start_v + "%<br>" + d.end_t + ":" + d.end_v + "%<br>Change:" + d.change + "%"
tooltip.html(t_text)
return tooltip.style("visibility", "visible");
})
.on("mousemove", function(){return tooltip.style("top", (event.pageY-10)+"px").style("left",(event.pageX+10)+"px");})
.on("mouseout", function(){return tooltip.style("visibility", "hidden");});
//define the circle
var circle = svg.selectAll("circle").data(my_data);
//add end circles (white fill, colour border, opacity=0)
circle.enter().append('circle')
.attr("cx",function(d){return x(d.end_v);})
.attr("cy",function(d){return y(d.label) + y.bandwidth()/2;})
.attr("r", '20')
.style("fill","white")
.style("stroke-width",'4')
.style("stroke",function (d) { return color(d.label); })
.on("mouseover", function(d){
//define tooltip
t_text = d.label + "<br>" + d.start_t + ":" + d.start_v + "%<br>" + d.end_t + ":" + d.end_v + "%<br>Change:" + d.change + "%"
tooltip.html(t_text)
return tooltip.style("visibility", "visible");
})
.on("mousemove", function(){return tooltip.style("top", (event.pageY-10)+"px").style("left",(event.pageX+10)+"px");})
.on("mouseout", function(){return tooltip.style("visibility", "hidden");});
//add end circles (colour fill, opacity 0.4)
circle.enter().append('circle')
.attr("cx",function(d){return x(d.start_v);})
.attr("cy",function(d){return y(d.label) + y.bandwidth()/2;})
.style("opacity",0.4)
.attr("r", '22')
.style("fill",function (d) {
//gradient for circle. (2nd half of circle has no color so transition smooth)
if(d.end_v>d.start_v){
gradient(color(d.label),'circ'+ d.id,"100%","0%","50%","0%",0,1)
}else{
gradient(color(d.label),'circ'+ d.id,"0%","100%","50%","0%",0,1)
}return "url(#circ" + d.id +")"; })
.on("mouseover", function(d){
//define tooltip
t_text = d.label + "<br>" + d.start_t + ":" + d.start_v + "%<br>" + d.end_t + ":" + d.end_v + "%<br>Change:" + d.change + "%"
tooltip.html(t_text)
return tooltip.style("visibility", "visible");
})
.on("mousemove", function(){return tooltip.style("top", (event.pageY-10)+"px").style("left",(event.pageX+10)+"px");})
.on("mouseout", function(){return tooltip.style("visibility", "hidden");});
//finally add text (percents) - first for the end circles
var text = svg.append("g")
.attr("class", "labels")
.selectAll("text")
.data(my_data)
.enter()
.append("text")
.attr("dx", function(d){return x(d.end_v)+2;})
.attr("dy", function(d){return y(d.label) + (y.bandwidth()/2)+2;})
.attr("text-anchor","middle")
.html(function(d){return d.end_v + "%";});
//then for the start circles
var text2 = svg.append("g")
.attr("class", "labels")
.selectAll("text")
.data(my_data)
.enter()
.append("text")
.attr("dx", function(d){return x(d.start_v)+2;})
.attr("dy", function(d){return y(d.label) + (y.bandwidth()/2)+2;})
.attr("text-anchor","middle")
.html(function(d){return d.start_v + "%";});
function get_data(root,r_scale){
var my_data = [],
x=0;
for (r in root){
//loop through the data, calculate change and populate my_data
start_t = root[r]['start'].t
start_v = root[r]['start'].v
end_t = root[r]['end'].t
end_v = root[r]['end'].v
change = Math.abs(start_v - end_v)
my_data.push({change: change,id: x,label: root[r].label, start_t: start_t, start_v: start_v,end_t: end_t,end_v: end_v})
x = x + 1
}
return my_data
};
});
{
"ID:1": {
"label": "Data Point 1",
"start": {
"t": "2007",
"v": 58
},
"end": {
"t": "2014",
"v": 68
}
},
"ID:2": {
"label": "Data Point 2",
"start": {
"t": "2007",
"v": 59
},
"end": {
"t": "2014",
"v": 35
}
},
"ID:3": {
"label": "Data Point 3",
"start": {
"t": "2007",
"v": 62
},
"end": {
"t": "2014",
"v": 97
}
},
"national": {
"label": "National Average",
"start": {
"t": "2007",
"v": 86
},
"end": {
"t": "2014",
"v": 62
}
},
"state": {
"label": "State Average",
"start": {
"t": "2007",
"v": 88
},
"end": {
"t": "2014",
"v": 59
}
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment