Stacked Bars

Animation design by Heer and Robertson. Colors and data generation inspired by Byron and Wattenberg.

Source Code

  1 var n = 4, // number of layers
  2     m = 64, // number of samples per layer
  3     data = d3.layout.stack()(stream_layers(n, m, .1)),
  4     color = d3.interpolateRgb("#aad", "#556");
  5 
  6 var p = 20,
  7     w = 960,
  8     h = 500 - .5 - p,
  9     mx = m,
 10     my = d3.max(data, function(d) {
 11       return d3.max(d, function(d) {
 12         return d.y0 + d.y;
 13       });
 14     }),
 15     mz = d3.max(data, function(d) {
 16       return d3.max(d, function(d) {
 17         return d.y;
 18       });
 19     }),
 20     x = function(d) { return d.x * w / mx; },
 21     y0 = function(d) { return h - d.y0 * h / my; },
 22     y1 = function(d) { return h - (d.y + d.y0) * h / my; },
 23     y2 = function(d) { return d.y * h / mz; }; // or `my` to not rescale
 24 
 25 var vis = d3.select("#chart")
 26   .append("svg:svg")
 27     .attr("width", w)
 28     .attr("height", h + p);
 29 
 30 var layers = vis.selectAll("g.layer")
 31     .data(data)
 32   .enter().append("svg:g")
 33     .style("fill", function(d, i) { return color(i / (n - 1)); })
 34     .attr("class", "layer");
 35 
 36 var bars = layers.selectAll("g.bar")
 37     .data(function(d) { return d; })
 38   .enter().append("svg:g")
 39     .attr("class", "bar")
 40     .attr("transform", function(d) { return "translate(" + x(d) + ",0)"; });
 41 
 42 bars.append("svg:rect")
 43     .attr("width", x({x: .9}))
 44     .attr("x", 0)
 45     .attr("y", h)
 46     .attr("height", 0)
 47   .transition()
 48     .delay(function(d, i) { return i * 10; })
 49     .attr("y", y1)
 50     .attr("height", function(d) { return y0(d) - y1(d); });
 51 
 52 var labels = vis.selectAll("text.label")
 53     .data(data[0])
 54   .enter().append("svg:text")
 55     .attr("class", "label")
 56     .attr("x", x)
 57     .attr("y", h + 6)
 58     .attr("dx", x({x: .45}))
 59     .attr("dy", ".71em")
 60     .attr("text-anchor", "middle")
 61     .text(function(d, i) { return i; });
 62 
 63 vis.append("svg:line")
 64     .attr("x1", 0)
 65     .attr("x2", w - x({x: .1}))
 66     .attr("y1", h)
 67     .attr("y2", h);
 68 
 69 function transitionGroup() {
 70   var group = d3.selectAll("#chart");
 71 
 72   group.select("#group")
 73       .attr("class", "first active");
 74 
 75   group.select("#stack")
 76       .attr("class", "last");
 77 
 78   group.selectAll("g.layer rect")
 79     .transition()
 80       .duration(500)
 81       .delay(function(d, i) { return (i % m) * 10; })
 82       .attr("x", function(d, i) { return x({x: .9 * ~~(i / m) / n}); })
 83       .attr("width", x({x: .9 / n}))
 84       .each("end", transitionEnd);
 85 
 86   function transitionEnd() {
 87     d3.select(this)
 88       .transition()
 89         .duration(500)
 90         .attr("y", function(d) { return h - y2(d); })
 91         .attr("height", y2);
 92   }
 93 }
 94 
 95 function transitionStack() {
 96   var stack = d3.select("#chart");
 97 
 98   stack.select("#group")
 99       .attr("class", "first");
100 
101   stack.select("#stack")
102       .attr("class", "last active");
103 
104   stack.selectAll("g.layer rect")
105     .transition()
106       .duration(500)
107       .delay(function(d, i) { return (i % m) * 10; })
108       .attr("y", y1)
109       .attr("height", function(d) { return y0(d) - y1(d); })
110       .each("end", transitionEnd);
111 
112   function transitionEnd() {
113     d3.select(this)
114       .transition()
115         .duration(500)
116         .attr("x", 0)
117         .attr("width", x({x: .9}));
118   }
119 }
 1 /* Inspired by Lee Byron's test data generator. */
 2 function stream_layers(n, m, o) {
 3   if (arguments.length < 3) o = 0;
 4   function bump(a) {
 5     var x = 1 / (.1 + Math.random()),
 6         y = 2 * Math.random() - .5,
 7         z = 10 / (.1 + Math.random());
 8     for (var i = 0; i < m; i++) {
 9       var w = (i / m - y) * z;
10       a[i] += x * Math.exp(-w * w);
11     }
12   }
13   return d3.range(n).map(function() {
14       var a = [], i;
15       for (i = 0; i < m; i++) a[i] = o + o * Math.random();
16       for (i = 0; i < 5; i++) bump(a);
17       return a.map(stream_index);
18     });
19 }
20 
21 /* Another layer generator using gamma distributions. */
22 function stream_waves(n, m) {
23   return d3.range(n).map(function(i) {
24     return d3.range(m).map(function(j) {
25         var x = 20 * j / m - i / 3;
26         return 2 * x * Math.exp(-.5 * x);
27       }).map(stream_index);
28     });
29 }
30 
31 function stream_index(d, i) {
32   return {x: i, y: Math.max(0, d)};
33 }
Copyright © 2011 Mike Bostock
Fork me on GitHub