Built with blockbuilder.org
xxxxxxxxxx
<head>
<meta charset="utf-8">
<script src="https://d3js.org/d3.v4.min.js"></script>
</head>
<body>
<div>
<label><input type="radio" name="mode" value="grouped" checked>Grouped</label>
<label><input type="radio" name="mode" value="stacked">Stacked</label>
</div>
<script>
var margin = {top: 20, right: 20, bottom: 20, left: 20},
width = 960 - margin.left - margin.right,
height = 500 - margin.top - margin.bottom;
var n = 10,
m = 5;
var duration = 500;
// Random data
// (10) [Array(5), Array(5), Array(5), Array(5), Array(5), Array(5), Array(5), Array(5), Array(5), Array(5)]
// 0: (5) [0.8976472929321404, 0.16527384338607165, 0.5102470192425659, 0.9127164580119789, 0.12280753488611529]
// 1: (5) [0.36324654194776396, 0.7564192480661098, 0.5087518603046575, 0.1921867799203365, 0.8480531388829291]
// ...
var data = d3.range(n).map(function() {
return d3.range(m).map(Math.random);
});
// Create the vertical layout layer used for the stacked
// (5) [Array(10), Array(10), Array(10), Array(10), Array(10)]
// 0: (10) [Array(2), Array(2), Array(2), Array(2), Array(2), ...
// 1: (10) [Array(2), Array(2), Array(2), Array(2), Array(2), ...
// ...
var layers = d3.stack().keys(d3.range(m))(data);
// IMPORTANT
// Max stack seeks for max absolute height
var max_stack = d3.max(layers, function(e) { return d3.max(e, function(d) { return d[1] - 0; }); });
// Max group seeks for max relative height
var max_group = d3.max(layers, function(e) { return d3.max(e, function(d) { return d[1] - d[0]; }); });
var x = d3.scaleBand()
.rangeRound([0, width])
.paddingInner(0.1)
.domain(d3.range(n));
var y = d3.scaleLinear()
.domain([0, max_group])
.rangeRound([height, 0]);
var color = d3.scaleOrdinal(d3.schemeCategory10);
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 +")");
// ADD groups (will not change)
var layer = svg.selectAll(".layer")
.data(layers)
.enter()
.append("g")
.attr("class", "layer")
// IMPORTANT: memorize the nested group (useful for children)
.attr("id", function(d, i) { return d.key; })
.style("fill", function(d, i) { return color(i); });
// ADD rects (will change!)
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);
function swap() {
this.value == "grouped" ? grouped(): stacked();
}
d3.selectAll("input").on("change", swap);
function grouped() {
// Adjust the Y-scale height
y.domain([0, max_group]);
rect
// 1st transition
.transition()
.duration(duration)
.delay(function(d, i) { return i * duration / n; })
.attr("x", function(d, i) {
// OPTION 1: use the parent's ID to retrieve the nested group
var gid = layers.indexOf(this.parentNode.__data__);
// OPTION 2: browse the original dataset
gid = this.parentNode.id;
return x(i) + gid * (x.bandwidth() / m);
})
.attr("width", x.bandwidth() / m)
// 2nd transition
.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 stacked() {
// Adjust the Y-scale height
y.domain([0, max_stack]);
rect
// 1st transition
.transition()
.duration(duration)
.delay(function(d, i) { return i * duration / n; })
.attr("y", function(d) { return y(d[1]); })
.attr("height", function(d) { return y(d[0]) - y(d[1]); })
// 2nd transition
.transition()
.attr("x", function(d, i) { return x(i); })
.attr("width", x.bandwidth());
}
// INIT with grouped mode
grouped();
</script>
</body>
https://d3js.org/d3.v4.min.js