Skip to content

Instantly share code, notes, and snippets.

@micahstubbs
Last active January 22, 2017 07:49
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save micahstubbs/c71d79c0ee2a1f73827c32f3a2972181 to your computer and use it in GitHub Desktop.
Save micahstubbs/c71d79c0ee2a1f73827c32f3a2972181 to your computer and use it in GitHub Desktop.
Stacked-to-Grouped | JSON Data, Vertical Bars
license: MIT
border: no
height: 500

switch between stacked and grouped layouts using sequenced transitions ✨

animations preserve object constancy and allow the user to follow the data across views

animation design by Heer and Robertson inspired by Byron and Wattenberg

this iteration draws vertical bars from static data

an ES2015 implementation in something like the airbnb style

inspired by Stacked-to-Grouped Bars from @mbostock

custom radio buttons from the codepen Pure CSS & HTML cross-browser elegant Radio Buttons from knitevision1

you can also view this set of experiments in github repo form

Stacked-to-Grouped | Generated Data, Vertical Bars
Stacked-to-Grouped | JSON Data, Vertical Bars
Stacked-to-Grouped | Generated Data, Horizontal Bars
Stacked-to-Grouped | JSON Data, Horizontal Bars
Stacked-to-Grouped | JSON Data, Horizontal Bars, String Series Names

[
{
"0": 0.8345658418026087,
"1": 2.16990473059691,
"2": 0.11225498002657552,
"3": 0.17486306808582253,
"name": "zero"
},
{
"0": 0.4547571633367139,
"1": 2.293639687510199,
"2": 0.11257016829714184,
"3": 0.2298141045051727,
"name": "one"
},
{
"0": 0.190595042313489,
"1": 2.2298279573365214,
"2": 0.1839374938020395,
"3": 0.3360952676772113,
"name": "two"
},
{
"0": 0.19738488101007876,
"1": 2.206696498843617,
"2": 0.17533589513765335,
"3": 0.5859787329034409,
"name": "three"
},
{
"0": 0.109208829080153,
"1": 1.9552149026588583,
"2": 0.11277306719085622,
"3": 0.9600889268112711,
"name": "four"
},
{
"0": 0.1061610735747754,
"1": 1.5905810961242883,
"2": 0.13741060343967204,
"3": 1.1570885127433614,
"name": "five"
},
{
"0": 0.16451368843759093,
"1": 1.2859246485063744,
"2": 0.10184544036173926,
"3": 1.1241807682264733,
"name": "six"
},
{
"0": 0.1025131649061017,
"1": 0.9717402103146218,
"2": 0.12968887820309827,
"3": 0.8727085031257851,
"name": "seven"
},
{
"0": 0.1949447302325289,
"1": 0.6992763765691113,
"2": 0.17971966572582898,
"3": 0.517827011694173,
"name": "eight"
},
{
"0": 0.1177193520601679,
"1": 0.46505701727659365,
"2": 0.11270388919011565,
"3": 0.3260373543877625,
"name": "nine"
},
{
"0": 0.1462059409009294,
"1": 0.3094175422555735,
"2": 0.16167759830492812,
"3": 0.1715670622073826,
"name": "ten"
},
{
"0": 0.1851331271609325,
"1": 0.24030077931859933,
"2": 0.1011320269253213,
"3": 0.18588128995552966,
"name": "eleven"
},
{
"0": 0.18106955819260212,
"1": 0.22449970770786298,
"2": 0.14461975744106742,
"3": 0.13603826586713308,
"name": "twelve"
},
{
"0": 0.17837297705138078,
"1": 0.18174301488576006,
"2": 0.15011967618163913,
"3": 0.17794511739155605,
"name": "thirteen"
},
{
"0": 0.18259683666081902,
"1": 0.1847859732797195,
"2": 0.17994292592532157,
"3": 0.19901139192343287,
"name": "fourteen"
},
{
"0": 0.1110130198686905,
"1": 0.1436178823525508,
"2": 0.10628027401737211,
"3": 0.1814916926446018,
"name": "fifteen"
},
{
"0": 0.11361529035333096,
"1": 0.15947784914079133,
"2": 0.11441050324234148,
"3": 0.13875245625206478,
"name": "sixteen"
},
{
"0": 0.14790163702068787,
"1": 0.19014909804324626,
"2": 0.1555474119041413,
"3": 0.20840404337267765,
"name": "seventeen"
},
{
"0": 0.1949968036066766,
"1": 0.29270545322394426,
"2": 0.114201446093528,
"3": 0.20711288514200954,
"name": "eighteen"
},
{
"0": 0.16061663536254375,
"1": 0.4488311604689811,
"2": 0.19108732592581135,
"3": 0.29196460596774787,
"name": "nineteen"
},
{
"0": 0.11828970638524117,
"1": 0.6022499142439799,
"2": 0.1906754956974005,
"3": 0.44976999290236486,
"name": "twenty"
},
{
"0": 0.19052420051116256,
"1": 0.7586339982956414,
"2": 0.18611717332528988,
"3": 0.7135240637821088,
"name": "twenty-one"
},
{
"0": 0.14102757049524564,
"1": 1.0436032784557587,
"2": 0.21383567141546456,
"3": 0.9105069969517476,
"name": "twenty-two"
},
{
"0": 0.1400763509851857,
"1": 1.2529114940133406,
"2": 0.16792258073092658,
"3": 1.2392922805597992,
"name": "twenty-three"
},
{
"0": 0.17145341173755935,
"1": 1.369028294902841,
"2": 0.2554756001305245,
"3": 1.4390036686572394,
"name": "twenty-four"
},
{
"0": 0.1142197822651475,
"1": 1.3204483212776335,
"2": 0.2512993182290364,
"3": 1.6674115370963114,
"name": "twenty-five"
},
{
"0": 0.15427104036012443,
"1": 1.2467959228758292,
"2": 0.4447079455959,
"3": 1.7608832243905004,
"name": "twenty-six"
},
{
"0": 0.18941420012129426,
"1": 0.9566411058342233,
"2": 0.6934709075960008,
"3": 1.6711918800408099,
"name": "twenty-seven"
},
{
"0": 0.16030801532958389,
"1": 0.72968388523596,
"2": 1.2456663863075754,
"3": 1.449647296489422,
"name": "twenty-eight"
},
{
"0": 0.1612989393999626,
"1": 0.5232221990536086,
"2": 2.0069308208914673,
"3": 1.2387292588529517,
"name": "twenty-nine"
},
{
"0": 0.1680850695478585,
"1": 0.4098415290607861,
"2": 2.8534512729434294,
"3": 0.9218047337208097,
"name": "thirty"
},
{
"0": 0.1167075659137578,
"1": 0.2243459400027773,
"2": 3.4631976204576547,
"3": 0.6533988575872596,
"name": "thirty-one"
},
{
"0": 0.14647393431706796,
"1": 0.2458810499577117,
"2": 3.4525744339678957,
"3": 0.4886764645805308,
"name": "thirty-two"
},
{
"0": 0.1278677185146747,
"1": 0.18830239395443316,
"2": 3.1065773115746826,
"3": 0.37642130506603877,
"name": "thirty-three"
},
{
"0": 0.1391673391797843,
"1": 0.11174942607313926,
"2": 2.7357288662136545,
"3": 0.23271637827985625,
"name": "thirty-four"
},
{
"0": 0.2507616269855116,
"1": 0.13015386121548111,
"2": 2.541133252201119,
"3": 0.22131639202239528,
"name": "thirty-five"
},
{
"0": 0.26073384485168427,
"1": 0.13446671655256653,
"2": 2.5342757106187426,
"3": 0.19129613570397597,
"name": "thirty-six"
},
{
"0": 0.3255208118456826,
"1": 0.14310755099573258,
"2": 2.531776208280289,
"3": 0.20346806297265152,
"name": "thirty-seven"
},
{
"0": 0.49053994714140703,
"1": 0.1833573284332347,
"2": 2.5515216319513385,
"3": 0.16097417163299932,
"name": "thirty-eight"
},
{
"0": 0.6566616225859324,
"1": 0.1420633692087001,
"2": 2.566489413584417,
"3": 0.19608224521048845,
"name": "thirty-nine"
},
{
"0": 0.7906099471022422,
"1": 0.13088816206384812,
"2": 2.658579431787212,
"3": 0.1449285983263784,
"name": "forty"
},
{
"0": 0.9807878060688358,
"1": 0.10349600956435734,
"2": 2.678643544449402,
"3": 0.14288310732988485,
"name": "forty-one"
},
{
"0": 1.158566848273416,
"1": 0.14552089136288648,
"2": 2.8595505453152823,
"3": 0.1201609266976762,
"name": "forty-two"
},
{
"0": 1.3158716481832025,
"1": 0.11165480642176173,
"2": 2.9227109679896137,
"3": 0.18328416679433346,
"name": "forty-three"
},
{
"0": 1.3141794328922924,
"1": 0.17506966697906257,
"2": 3.0551860629320835,
"3": 0.17020174336390603,
"name": "forty-four"
},
{
"0": 1.2835620009724025,
"1": 0.15243850480914034,
"2": 3.149823756134488,
"3": 0.19692156360240798,
"name": "forty-five"
},
{
"0": 1.1763590731124858,
"1": 0.10565073069580855,
"2": 3.085488798263314,
"3": 0.10721725212540267,
"name": "forty-six"
},
{
"0": 1.0247175741491994,
"1": 0.13738228419716658,
"2": 2.9246150406007576,
"3": 0.1632273588191122,
"name": "forty-seven"
},
{
"0": 0.8811955960917054,
"1": 0.12676860140735136,
"2": 2.674648207968276,
"3": 0.17207439227140098,
"name": "forty-eight"
},
{
"0": 0.7063281912406454,
"1": 0.16015511531309168,
"2": 2.3763529091188613,
"3": 0.16109040013513898,
"name": "forty-nine"
},
{
"0": 0.5449865739415336,
"1": 0.19760568934581513,
"2": 2.0049745878747496,
"3": 0.30556676529604854,
"name": "fifty"
},
{
"0": 0.4121531564634099,
"1": 0.11815561152617993,
"2": 1.599948087918452,
"3": 0.5647793548545561,
"name": "fifty-one"
},
{
"0": 0.32214329647797335,
"1": 0.12102494255479967,
"2": 1.2459420697339225,
"3": 1.0819651957138756,
"name": "fifty-two"
},
{
"0": 0.27217968508738444,
"1": 0.19183100050676083,
"2": 0.8911795060576224,
"3": 1.831423637178713,
"name": "fifty-three"
},
{
"0": 0.1861140478814755,
"1": 0.1623990293221626,
"2": 0.642617369968976,
"3": 2.401284906380081,
"name": "fifty-four"
},
{
"0": 0.12542299658774217,
"1": 0.14343688785307282,
"2": 0.49382970229231415,
"3": 2.2719496090564806,
"name": "fifty-five"
},
{
"0": 0.14181692551789446,
"1": 0.18442442382104077,
"2": 0.3469738553459,
"3": 1.6072251382480411,
"name": "fifty-six"
},
{
"0": 0.1905082555552165,
"1": 0.15980815514017616,
"2": 0.26596917763541894,
"3": 0.9092048653077827,
"name": "fifty-seven"
}
]
<!DOCTYPE html>
<meta charset='utf-8'>
<style>
/* Radio button */
.custom-radio input[type="radio"] {
display:none;
}
.custom-radio input[type="radio"] + label {
font-size:14px;
color: black;
cursor: pointer;
}
.custom-radio input[type="radio"] + label span {
display:inline-block;
width:10px;
height:10px;
margin:-1px 4px 0 0;
vertical-align:middle;
cursor:pointer;
-moz-border-radius: 50%;
border-radius: 50%;
}
.custom-radio input[type="radio"] + label span {
background-color: #c4c4c4; /* have to be of the same color */
border: 2px solid #c4c4c4; /* have to be of the same color */
}
/* Checked state for radio */
.custom-radio input[type='radio']:checked + label span{
background-color: #31A354;
}
</style>
<div class='custom-radio'>
<div>
<input type='radio' id='grouped' name='mode' value='grouped'>
<label for='grouped'><span></span> Grouped</label>
</div>
<div>
<input type='radio' id='stacked' name='mode' value='stacked' checked>
<label for='stacked'><span></span> Stacked</label>
</div>
</div>
<svg width='960' height='500'></svg>
<script src='https://d3js.org/d3.v4.min.js'></script>
<script src='https://d3js.org/d3-queue.v2.min.js'></script>
<script src='https://cdnjs.cloudflare.com/ajax/libs/babel-standalone/6.21.1/babel.min.js'></script>
<script src='vis.js' lang='babel' type='text/babel'></script>
const queue = d3_queue.queue();
queue
.defer(d3.json, 'data.json')
.await(render);
function render(error, data) {
const seriesKeys = Object.keys(data[0]).slice(0, Object.keys(data[0]).length - 1);
console.log('Object.keys(data[0])', Object.keys(data[0]));
console.log('seriesKeys', seriesKeys);
const stackedData = d3.stack()
.keys(seriesKeys)(data);
const yMaxGrouped = d3.max(data, d => d3.max(Object.values(d)));
const yMaxStacked = d3.max(data, d => d3.sum(Object.values(d)));
const n = Object.keys(data[0]).length; // the number of series
const m = d3.range(data.length); // the number of values per series
console.log('stackedData', stackedData);
console.log('yMaxGrouped', yMaxGrouped);
console.log('yMaxStacked', yMaxStacked);
console.log('n, the number of series', n);
console.log('m, the number of values per series', m);
const svg = d3.select('svg');
const controlHeight = 50;
const margin = {top: 10, right: 10, bottom: 20, left: 10};
const width = +svg.attr('width') - margin.left - margin.right;
const height = +svg.attr('height') - controlHeight - margin.top - margin.bottom;
const g = svg.append('g')
.attr('transform', `translate(${margin.left},${margin.top})`);
const x = d3.scaleBand()
.domain(m)
.rangeRound([0, width])
.padding(0.08);
const y = d3.scaleLinear()
.domain([0, yMaxStacked])
.range([height, 0]);
const color = d3.scaleOrdinal()
.domain(d3.range(n))
.range(d3.schemeCategory20c.slice(8, 12)); // greens
const series = g.selectAll('.series')
.data(stackedData)
.enter().append('g')
.attr('fill', (d, i) => color(i));
const rect = series.selectAll('rect')
.data(d => d)
.enter().append('rect')
.attr('x', (d, i) => x(i))
.attr('y', height)
.attr('width', x.bandwidth())
.attr('height', 0);
rect.transition()
.delay((d, i) => i * 10)
.attr('y', d => y(d[1]))
.attr('height', d => y(d[0]) - y(d[1]));
g.append('g')
.attr('class', 'axis axis--x')
.attr('transform', `translate(0,${height})`)
.call(d3.axisBottom(x)
.tickSize(0)
.tickPadding(6));
d3.selectAll('input')
.on('change', changed);
const timeout = d3.timeout(() => {
d3.select('input[value=\'grouped\']')
.property('checked', true)
.dispatch('change');
}, 2000);
function changed() {
timeout.stop();
if (this.value === 'grouped') transitionGrouped();
else transitionStacked();
}
function transitionGrouped() {
y.domain([0, yMaxGrouped]);
rect.transition()
.duration(500)
.delay((d, i) => i * 10)
.attr('x', function(d, i) {
return x(i) + x.bandwidth() / n * this.parentNode.__data__.key;
})
.attr('width', x.bandwidth() / n)
.transition()
.attr('y', d => y(d[1] - d[0]))
.attr('height', d => y(0) - y(d[1] - d[0]));
}
function transitionStacked() {
y.domain([0, yMaxStacked]);
rect.transition()
.duration(500)
.delay((d, i) => i * 10)
.attr('y', d => y(d[1]))
.attr('height', d => y(d[0]) - y(d[1]))
.transition()
.attr('x', (d, i) => x(i))
.attr('width', x.bandwidth());
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment