Created
August 8, 2017 19:50
-
-
Save abhannan/8e33705e23336ec29b04fec91297334d to your computer and use it in GitHub Desktop.
Waterfall Chart with D3 V4
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
<body> | |
<svg class="chart"></svg> | |
</body> | |
<style> | |
.bar.total rect { | |
fill: steelblue; | |
} | |
.bar.positive rect { | |
fill: darkolivegreen; | |
} | |
.bar.negative rect { | |
fill: crimson; | |
} | |
.bar line.connector { | |
stroke: grey; | |
stroke-dasharray: 3; | |
} | |
.bar text { | |
fill: white; | |
font: 12px sans-serif; | |
text-anchor: middle; | |
} | |
.axis text { | |
font: 10px sans-serif; | |
} | |
.axis path, | |
.axis line { | |
fill: none; | |
stroke: #000; | |
shape-rendering: crispEdges; | |
} | |
</style> | |
<script src="https://d3js.org/d3.v4.min.js"></script> | |
<script> | |
var margin = { | |
top: 20, | |
right: 30, | |
bottom: 30, | |
left: 40 | |
}, | |
width = 960 - margin.left - margin.right, | |
height = 500 - margin.top - margin.bottom, | |
padding = 0.3; | |
var x = d3.scaleBand() | |
.rangeRound([0, width]) | |
.padding(padding); | |
var y = d3.scaleLinear() | |
.range([height, 0]); | |
var xAxis = d3.axisBottom(x) | |
var yAxis = d3.axisLeft(y) | |
.tickFormat(function(d) { | |
return dollarFormatter(d); | |
}); | |
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 + ")"); | |
d3.csv("waterfall.csv", convertNumber, function(error, data) { | |
console.log(data) | |
// Transform data (i.e., finding cumulative values and total) for easier charting | |
var cumulative = 0; | |
for (var i = 0; i < data.length; i++) { | |
data[i].start = cumulative; | |
cumulative += data[i].value; | |
data[i].end = cumulative; | |
data[i].class = (data[i].value >= 0) ? 'positive' : 'negative' | |
} | |
data.push({ | |
name: 'Total', | |
end: cumulative, | |
start: 0, | |
class: 'total' | |
}); | |
x.domain(data.map(function(d) { | |
return d.name; | |
})); | |
y.domain([0, d3.max(data, function(d) { | |
return d.end; | |
})]); | |
chart.append("g") | |
.attr("class", "x axis") | |
.attr("transform", "translate(0," + height + ")") | |
.call(xAxis); | |
chart.append("g") | |
.attr("class", "y axis") | |
.call(yAxis); | |
var bar = chart.selectAll(".bar") | |
.data(data) | |
.enter().append("g") | |
.attr("class", function(d) { | |
return "bar " + d.class | |
}) | |
.attr("transform", function(d) { | |
return "translate(" + x(d.name) + ",0)"; | |
}); | |
bar.append("rect") | |
.attr("y", function(d) { | |
return y(Math.max(d.start, d.end)); | |
}) | |
.attr("height", function(d) { | |
return Math.abs(y(d.start) - y(d.end)); | |
}) | |
.attr("width", x.rangeBand()); | |
bar.append("text") | |
.attr("x", x.rangeBand() / 2) | |
.attr("y", function(d) { | |
return y(d.end) + 5; | |
}) | |
.attr("dy", function(d) { | |
return ((d.class == 'negative') ? '-' : '') + ".75em" | |
}) | |
.text(function(d) { | |
return dollarFormatter(d.end - d.start); | |
}); | |
bar.filter(function(d) { | |
return d.class != "total" | |
}).append("line") | |
.attr("class", "connector") | |
.attr("x1", x.rangeBand() + 5) | |
.attr("y1", function(d) { | |
return y(d.end) | |
}) | |
.attr("x2", x.rangeBand() / (1 - padding) - 5) | |
.attr("y2", function(d) { | |
return y(d.end) | |
}) | |
}); | |
function convertNumber(d) { | |
d.value = +d.value; | |
return d; | |
} | |
</script> |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment