Skip to content

Instantly share code, notes, and snippets.

@willzjc
Last active January 12, 2018 12:59
Show Gist options
  • Save willzjc/918be116a5523a02221e14939b109391 to your computer and use it in GitHub Desktop.
Save willzjc/918be116a5523a02221e14939b109391 to your computer and use it in GitHub Desktop.
Transition Stacked-to-Grouped Bars
license: gpl-3.0
<!DOCTYPE html>
<meta charset="utf-8">
<style>
body {
font-family: "Helvetica Neue", Helvetica, Arial, sans-serif;
margin: auto;
position: relative;
width: 960px;
}
text {
font: 10px sans-serif;
}
.axis path,
.axis line {
fill: none;
stroke: #000;
shape-rendering: crispEdges;
}
form {
position: absolute;
right: 10px;
top: 10px;
}
</style>
<form>
<label><input type="radio" name="mode" value="grouped"> Grouped</label>
<label><input type="radio" name="mode" value="stacked" checked> Stacked</label>
</form>
<script src="//d3js.org/d3.v4.min.js"></script>
<script>
var n = 4, // number of layers
m = 58, // number of samples per layer
stack = d3.stack(),
data = d3.range(n).map(function() { return bumpLayer(m, .1); });
// transpose data
data = data[0].map(function(col, i) {
return data.map(function(row) {
return row[i]
})
});
var layers = stack.keys(d3.range(n))(data),
yStackMax = d3.max(layers, function(layer) { return d3.max(layer, function(d) { return d[1]; }); }),
yGroupMax = d3.max(layers, function(layer) { return d3.max(layer, function(d) { return d[1] - d[0]; }); });
var margin = {top: 40, right: 10, bottom: 20, left: 10},
width = 960 - margin.left - margin.right,
height = 500 - margin.top - margin.bottom;
var x = d3.scaleBand()
.domain(d3.range(m))
.rangeRound([0, width])
.padding(0.1)
.align(0.1);
var y = d3.scaleLinear()
.domain([0, yStackMax])
.rangeRound([height, 0]);
var color = d3.scaleLinear()
.domain([0, n - 1])
.range(["#aad", "#556"]);
var xAxis = d3.axisBottom()
.scale(x)
.tickSize(0)
.tickPadding(6);
var svg = d3.select("body").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 + ")");
var layer = svg.selectAll(".layer")
.data(layers)
.enter().append("g")
.attr("class", "layer")
.attr("id", function(d) { return d.key; })
.style("fill", function(d, i) { return color(i); });
var rect = layer.selectAll("rect")
.data(function(d) { return d; })
.enter().append("rect")
.attr("x", function(d, i) { return x(i); })
.attr("y", height)
.attr("width", x.bandwidth())
.attr("height", 0);
rect.transition()
.delay(function(d, i) {return i * 10; })
.attr("y", function(d) { return y(d[1]); })
.attr("height", function(d) { return y(d[0]) - y(d[1]); });
svg.append("g")
.attr("class", "x axis")
.attr("transform", "translate(0," + height + ")")
.call(xAxis);
d3.selectAll("input").on("change", change);
var timeout = setTimeout(function() {
d3.select("input[value=\"grouped\"]").property("checked", true).each(change);
}, 2000);
function change() {
clearTimeout(timeout);
if (this.value === "grouped") transitionGrouped();
else transitionStacked();
}
function transitionGrouped() {
y.domain([0, yGroupMax]);
rect.transition()
.duration(500)
.delay(function(d, i) { return i * 10; })
.attr("x", function(d, i, j) { return x(i) + x.bandwidth() / n * parseInt(this.parentNode.id); })
.attr("width", x.bandwidth() / n)
.transition()
.attr("y", function(d) { return height - (y(d[0]) - y(d[1])); })
.attr("height", function(d) { return y(d[0]) - y(d[1]); });
}
function transitionStacked() {
y.domain([0, yStackMax]);
rect.transition()
.duration(500)
.delay(function(d, i) { return i * 10; })
.attr("y", function(d) { return y(d[1]); })
.attr("height", function(d) { return y(d[0]) - y(d[1]); })
.transition()
.attr("x", function(d, i) { return x(i); })
.attr("width", x.bandwidth());
}
// Inspired by Lee Byron's test data generator.
function bumpLayer(n, o) {
function bump(a) {
var x = 1 / (.1 + Math.random()),
y = 2 * Math.random() - .5,
z = 10 / (.1 + Math.random());
for (var i = 0; i < n; i++) {
var w = (i / n - y) * z;
a[i] += x * Math.exp(-w * w);
}
}
var a = [], i;
for (i = 0; i < n; ++i) a[i] = o + o * Math.random();
for (i = 0; i < 5; ++i) bump(a);
return a.map(function(d, i) { return Math.max(0, d); });
}
</script>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment