Skip to content

Instantly share code, notes, and snippets.

@HarryStevens
Last active Oct 9, 2017
Embed
What would you like to do?
Stacked Bar Update Pattern
license: gpl-3.0

Use D3's general update pattern to animate stacked bar chart transitions.

Data shown here is in this format:

name a b c d e f sum
Ann 8 6 3 6 1 1 25
Bob 1 9 10 1 5 8 34
Jean 3 4 3 2 1 8 21
Chuck 2 3 5 8 5 9 32
Denise 9 10 3 8 8 8 46
Eric 3 9 2 4 10 4 32
Frida 6 6 3 7 10 6 38

Axis styling from this block. Colors from ColorBrewer.

<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<style>
body {
margin: 0;
}
.y.axis .domain {
display: none;
}
</style>
</head>
<body>
<script src="https://d3js.org/d3.v4.min.js"></script>
<script src="https://unpkg.com/jeezy/lib/jeezy.min.js"></script>
<script src="https://unpkg.com/d3-marcon/build/d3-marcon.min.js"></script>
<script>
var x_var = "name";
var alphabet = "abcdef".split("");
var names = ["Ann", "Bob", "Jean", "Chuck", "Denise", "Eric", "Frida", "Greg", "Hillary"];
var setup = d3.marcon()
.top(20)
.bottom(20)
.left(10)
.right(10)
.width(window.innerWidth)
.height(window.innerHeight);
setup.render();
var width = setup.innerWidth(), height = setup.innerHeight(), svg = setup.svg();
var color = d3.scaleOrdinal(["#66c2a5","#fc8d62","#8da0cb","#e78ac3","#a6d854","#ffd92f"])
var x = d3.scaleBand()
.rangeRound([0, width])
.domain(names)
.padding(.25);
var y = d3.scaleLinear()
.rangeRound([height, 0]);
var x_axis = d3.axisBottom(x);
var y_axis = d3.axisRight(y)
.tickSize(width)
.tickFormat(function(d, i, ticks){ return i == ticks.length - 1 ? d + " value" : d; });
svg.append("g")
.attr("class", "x axis")
.attr("transform", "translate(0," + height + ")")
.call(x_axis);
svg.append("g")
.attr("class", "y axis")
.call(customYAxis);
var stack = d3.stack()
.keys(alphabet)
.order(d3.stackOrderNone)
.offset(d3.stackOffsetNone);
redraw(randomData());
d3.interval(function(){
redraw(randomData());
}, 1000);
function redraw(data){
// update the y scale
y.domain([0, jz.arr.max(data.map(function(d){ return d.sum }))]);
svg.select(".y")
.transition()
.call(customYAxis);
// each data column (a.k.a "key" or "series") needs to be iterated over
// the variable alphabet represents the unique keys of the stacks
alphabet.forEach(function(key, key_index){
var bar = svg.selectAll(".bar-" + key)
.data(stack(data)[key_index], function(d){ return d.data[x_var] + "-" + key; });
bar
.transition()
.attr("x", function(d){ return x(d.data[x_var]); })
.attr("y", function(d){ return y(d[1]); })
.attr("height", function(d){ return y(d[0]) - y(d[1]); });
bar.enter().append("rect")
.attr("class", function(d){ return "bar bar-" + key; })
.attr("x", function(d){ return x(d.data[x_var]); })
.attr("y", function(d){ return y(d[1]); })
.attr("height", function(d){ return y(d[0]) - y(d[1]); })
.attr("width", x.bandwidth())
.attr("fill", function(d){ return color(key); })
});
}
function randomData(data){
return names.map(function(d){
var obj = {};
obj.name = d;
var nums = [];
alphabet.forEach(function(e){
var num = jz.num.randBetween(1, 10);
obj[e] = num;
nums.push(num);
});
obj.sum = jz.arr.sum(nums);
return obj;
});
}
function customYAxis(g) {
g.call(y_axis);
g.selectAll(".tick:not(:first-of-type) line").attr("stroke", "#777").attr("stroke-dasharray", "2,2");
g.selectAll(".tick text").attr("x", 4).attr("dy", -4);
}
</script>
</body>
</html>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment