I took the grouped bar chart example and added two kinds of transitions.
Last active
May 24, 2017 07:59
-
-
Save aholachek/fb0c1cd7ea9707bc8ff55a82402c54b1 to your computer and use it in GitHub Desktop.
Animated Grouped/Stacked Bar Transitions
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/*eslint no-undef: 0*/ | |
function createChart (svg, data) { | |
// //normalize data | |
// Object.keys(data).forEach((d)=>{ | |
// ["0", "1", "2", "3", "4", "5", "6"].forEach(k=>{ | |
// if (d[k] === undefined) d[k] = | |
// }) | |
// }) | |
var colors = ['#98abc5', '#8a89a6', '#7b6888', '#6b486b', '#a05d56', '#d0743c', '#ff8c00'] | |
svg = d3.select(svg) | |
var margin = {top: 20, right: 20, bottom: 30, left: 40} | |
var width = 960 - margin.left - margin.right | |
var height = 500 - margin.top - margin.bottom | |
var g = svg.append('g').attr('transform', 'translate(' + margin.left + ',' + margin.top + ')') | |
var x0 = d3.scaleBand() | |
.rangeRound([0, width]) | |
.paddingInner(0.1) | |
var x1 = d3.scaleBand() | |
.padding(0.05) | |
var y = d3.scaleLinear() | |
.rangeRound([height, 0]) | |
var z = d3.scaleOrdinal() | |
.range(colors) | |
// check each subset of data for possible sections, since not all subsets have every possible section. | |
var nameKeys = data[Object.keys(data)[0]].map(function (obj) { return obj.name; }) | |
var valueKeys = ["1", "2", "3", "4", "5", "6"] | |
//fill in empty data entries | |
Object.keys(data).forEach(function (d){ | |
data[d].forEach(function (section){ | |
valueKeys.forEach(function (k){ | |
if (section.values[k] === undefined) { section.values[k] = 0 } | |
}) | |
}) | |
}) | |
x0.domain(nameKeys) | |
x1.domain(valueKeys).rangeRound([0, x0.bandwidth()]) | |
var barContainer = g.append('g') | |
var xAxis = g.append('g') | |
.attr('class', 'axis') | |
.attr('transform', 'translate(0,' + height + ')') | |
.call(d3.axisBottom(x0)) | |
var yAxis = g.append('g') | |
.attr('class', 'axis') | |
yAxis | |
.append('text') | |
.attr('x', 2) | |
.attr('y', y(y.ticks().pop()) + 0.5) | |
.attr('dy', '0.32em') | |
.attr('font-weight', 'bold') | |
.attr('text-anchor', 'start') | |
.text('Prop Value') | |
var legend = g.append('g') | |
.attr('font-size', 10) | |
.attr('text-anchor', 'end') | |
legend.append('text') | |
.text('Loan Grade') | |
.attr('x', width - 19) | |
.style('font-weight', 'bold') | |
.attr('dy', -10) | |
.attr('dx', 20) | |
var legendEnter = legend | |
.selectAll('g') | |
.data(valueKeys) | |
.enter().append('g') | |
.attr('transform', function (d, i) { return 'translate(0,' + i * 20 + ')' }) | |
legendEnter.append('rect') | |
.attr('x', width - 19) | |
.attr('width', 19) | |
.attr('height', 19) | |
.attr('fill', z) | |
legendEnter.append('text') | |
.attr('x', width - 24) | |
.attr('y', 9.5) | |
.attr('dy', '0.32em') | |
.text(function (d) { return d; }) | |
var stack = d3.stack() | |
.keys(valueKeys) | |
function updateChart (data, chartType) { | |
if ( chartType === void 0 ) chartType='group'; | |
if (chartType === 'group'){ | |
//find max value of a section | |
var maxValue = d3.max(data.map(function (d) { return Object.values(d.values); }).reduce(function (a, b) { return a.concat(b); }, [])) | |
y.domain([0, maxValue]).nice() | |
yAxis.transition() | |
.call(d3.axisLeft(y)) | |
var barsWithData = barContainer | |
.selectAll('g') | |
.data(data) | |
barsWithData.exit().remove() | |
var bars = barsWithData | |
.enter() | |
.append('g') | |
.attr('transform', function (d) { return 'translate(' + x0(d.name) + ',0)' }) | |
.merge(barsWithData) | |
.selectAll('rect') | |
.data(function (d) { | |
return Object.keys(d.values).map(function (k) { return ({ key: k, value: d.values[k] }); }) | |
}) | |
bars.exit().transition().style('opacity', 0).remove() | |
bars | |
.enter() | |
.append('rect') | |
.attr('fill', function (d) { | |
return z(d.key) | |
}) | |
// start y at height (0) so animation in looks like bars are growing upwards | |
.attr('y', height) | |
.merge(bars) | |
.transition() | |
.attr('width', x1.bandwidth()) | |
.attr('x', function (d) { return x1(d.key) }) | |
.attr('y', function (d) { return y(d.value); }) | |
.attr('height', function (d) { return height - y(d.value); }) | |
} | |
// ======================================================== | |
// show stacked view | |
// ======================================================== | |
else if (chartType === 'stack'){ | |
//find max value of a section | |
var maxValue$1 = d3.max( | |
data.map(function (d) { return Object.values(d.values); }) | |
.map(function (valueArray){ | |
return valueArray.reduce(function (a,b){ return a+ b; }) | |
}) | |
) | |
y.domain([0, maxValue$1]).nice() | |
yAxis.transition() | |
.call(d3.axisLeft(y)) | |
//add data for missing bars | |
var seriesFlipped = stack(data.map(function (d){ | |
var defaultData = {} | |
valueKeys.forEach(function (k){ return defaultData[k] = 0; }) | |
return Object.assign(defaultData, d.values) | |
})) | |
var series = [] | |
//need to reorient the series | |
//we want a list of groups, not a list of rects from each level | |
seriesFlipped[0].forEach(function (col, i){ | |
var arr = [] | |
seriesFlipped.forEach(function (row, index2){ | |
//mimic the key from the grouped data format | |
row[i].key = index2 + 1 + '' | |
arr.push(row[i]) | |
}) | |
series.push(arr) | |
}) | |
var barSections = barContainer | |
.selectAll('g') | |
.data(series) | |
var bars$1 = barSections | |
.enter() | |
.append('g') | |
.merge(barSections) | |
.attr('transform', function (d,i){console.log(x0(nameKeys[i])); return 'translate(' + x0(nameKeys[i]) + ',0)'} ) | |
.selectAll('rect') | |
.data(function (d){ return d; }, function (d){ return d.key; }) | |
var enterBars = bars$1.enter().append('rect') | |
.attr('fill', function (d){ return z(d.key); }) | |
bars$1.exit().transition().style('opacity', 0).remove() | |
enterBars | |
.merge(bars$1) | |
.transition() | |
.delay(function (d,i){ return i * 50; }) | |
.attr('width', x0.bandwidth()) | |
.attr("y", function(d) {return y(d[1]) }) | |
.attr("x", 0) | |
.attr("height", function(d) { return y(d[0]) - y(d[1]) }) | |
} | |
} | |
return { | |
updateChart: updateChart | |
} | |
} | |
d3.json('./data.json', function(error, data){ | |
//start with the first year selected | |
var chart = createChart(document.querySelector('svg'), data) | |
// append the input controls | |
var fieldset1 = d3.select('.controls').append('fieldset') | |
fieldset1.append('legend').text('Year') | |
Object.keys(data).forEach(function (year, index ){ | |
var label = fieldset1.append('label') | |
label | |
.append('input') | |
.attr('type', 'radio') | |
.attr('name', 'year') | |
.attr('value', year) | |
.attr('checked', function(){ | |
if (index === 0) { return true } | |
return null | |
}) | |
label.append('span') | |
.text(year) | |
label.on('click', function(){ | |
chart.updateChart(data[year], document.querySelector('input[name="graphType"]:checked').value) | |
}) | |
}) | |
var fieldset2 = d3.select('.controls').append('fieldset') | |
var types = ['group', 'stack'] | |
fieldset2.append('legend').text('Graph Layout') | |
types.forEach(function (graphType, index){ | |
var label = fieldset2.append('label') | |
label.append('input') | |
.attr('type', 'radio') | |
.attr('name', 'graphType') | |
.attr('value', graphType) | |
.attr('checked', function(){ | |
if (index === 0) { return true } | |
return null | |
}) | |
.on('click', function (){ | |
chart.updateChart(data[document.querySelector('input[name="year"]:checked').value], graphType) | |
}) | |
label.append('span') | |
.text(graphType) | |
}) | |
// render initial chart | |
chart.updateChart(data[Object.keys(data)[0]]) | |
}) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
{ | |
"2007": [{ | |
"name": "< 680", | |
"values": { | |
"2": 0.001496073, | |
"3": 0.126923915, | |
"4": 0.561200924, | |
"5": 0.759201816, | |
"6": 0.893993805 | |
} | |
}, { | |
"name": "680-719", | |
"values": { | |
"1": 0.001278119, | |
"2": 0.362579479, | |
"3": 0.726073687, | |
"4": 0.360936984, | |
"5": 0.208819673, | |
"6": 0.106006195 | |
} | |
}, { | |
"name": "720-759", | |
"values": { | |
"1": 0.292816973, | |
"2": 0.454058097, | |
"3": 0.114257685, | |
"4": 0.04486968, | |
"5": 0.03197851 | |
} | |
}, { | |
"name": "760-799", | |
"values": { | |
"1": 0.577965235, | |
"2": 0.150698167, | |
"3": 0.032744713, | |
"4": 0.032992412 | |
} | |
}, { | |
"name": " > 799", | |
"values": { | |
"1": 0.127939673, | |
"2": 0.031168184 | |
} | |
}], | |
"2008": [{ | |
"name": "< 680", | |
"values": { | |
"3": 0.143311078, | |
"4": 0.535939085, | |
"5": 0.723523376, | |
"6": 0.908838126 | |
} | |
}, { | |
"name": "680-719", | |
"values": { | |
"1": 0.004053058, | |
"2": 0.389601129, | |
"3": 0.755033279, | |
"4": 0.432991983, | |
"5": 0.26300794, | |
"6": 0.080496991 | |
} | |
}, { | |
"name": "720-759", | |
"values": { | |
"1": 0.493578271, | |
"2": 0.465804997, | |
"3": 0.09097742, | |
"4": 0.02912365, | |
"5": 0.013468684, | |
"6": 0.010664883 | |
} | |
}, { | |
"name": "760-799", | |
"values": { | |
"1": 0.426650174, | |
"2": 0.139293212, | |
"3": 0.00653134, | |
"4": 0.001945282 | |
} | |
}, { | |
"name": " > 799", | |
"values": { | |
"1": 0.075718497, | |
"2": 0.005300662, | |
"3": 0.004146883 | |
} | |
}], | |
"2009": [{ | |
"name": "< 680", | |
"values": { | |
"2": 0.001972273, | |
"3": 0.061021424, | |
"4": 0.347816086, | |
"5": 0.61161081, | |
"6": 0.570951163 | |
} | |
}, { | |
"name": "680-719", | |
"values": { | |
"1": 0.018202872, | |
"2": 0.293262861, | |
"3": 0.758535486, | |
"4": 0.513630234, | |
"5": 0.31274635, | |
"6": 0.350945594 | |
} | |
}, { | |
"name": "720-759", | |
"values": { | |
"1": 0.483340134, | |
"2": 0.536493435, | |
"3": 0.147870432, | |
"4": 0.099536218, | |
"5": 0.061560763, | |
"6": 0.065444695 | |
} | |
}, { | |
"name": "760-799", | |
"values": { | |
"1": 0.426668429, | |
"2": 0.136695924, | |
"3": 0.030099845, | |
"4": 0.039017462, | |
"5": 0.014082076 | |
} | |
}, { | |
"name": " > 799", | |
"values": { | |
"1": 0.071788565, | |
"2": 0.031575507, | |
"3": 0.002472813, | |
"6": 0.012658548 | |
} | |
}], | |
"2010": [{ | |
"name": "< 680", | |
"values": { | |
"2": 0.000565286, | |
"3": 0.101189409, | |
"4": 0.322693727, | |
"5": 0.460132061, | |
"6": 0.768736028 | |
} | |
}, { | |
"name": "680-719", | |
"values": { | |
"1": 0.027164145, | |
"2": 0.299532019, | |
"3": 0.663901042, | |
"4": 0.619509592, | |
"5": 0.522816687, | |
"6": 0.220272267 | |
} | |
}, { | |
"name": "720-759", | |
"values": { | |
"1": 0.529838118, | |
"2": 0.540840914, | |
"3": 0.212721062, | |
"4": 0.053869384, | |
"5": 0.014355666, | |
"6": 0.007426827 | |
} | |
}, { | |
"name": "760-799", | |
"values": { | |
"1": 0.4036741, | |
"2": 0.141882155, | |
"3": 0.018212217, | |
"4": 0.003484646, | |
"5": 0.002695586, | |
"6": 0.003564877 | |
} | |
}, { | |
"name": " > 799", | |
"values": { | |
"1": 0.039323637, | |
"2": 0.017179627, | |
"3": 0.00397627, | |
"4": 0.000442651 | |
} | |
}], | |
"2011": [{ | |
"name": "< 680", | |
"values": { | |
"2": 0.015057048, | |
"3": 0.116486357, | |
"4": 0.224473482, | |
"5": 0.289184506, | |
"6": 0.637609798 | |
} | |
}, { | |
"name": "680-719", | |
"values": { | |
"1": 0.067014875, | |
"2": 0.386027401, | |
"3": 0.416716479, | |
"4": 0.568125117, | |
"5": 0.61540113, | |
"6": 0.348747984 | |
} | |
}, { | |
"name": "720-759", | |
"values": { | |
"1": 0.539048116, | |
"2": 0.432319314, | |
"3": 0.384647352, | |
"4": 0.186473585, | |
"5": 0.088463897, | |
"6": 0.013642218 | |
} | |
}, { | |
"name": "760-799", | |
"values": { | |
"1": 0.347640699, | |
"2": 0.151553784, | |
"3": 0.075187561, | |
"4": 0.01983747, | |
"5": 0.006950466 | |
} | |
}, { | |
"name": " > 799", | |
"values": { | |
"1": 0.04629631, | |
"2": 0.015042453, | |
"3": 0.006962251, | |
"4": 0.001090345 | |
} | |
}], | |
"2012": [{ | |
"name": "< 680", | |
"values": { | |
"1": 0.005421439, | |
"2": 0.063906321, | |
"3": 0.283911481, | |
"4": 0.425256631, | |
"5": 0.450367247, | |
"6": 0.759740613 | |
} | |
}, { | |
"name": "680-719", | |
"values": { | |
"1": 0.144858351, | |
"2": 0.634091318, | |
"3": 0.567030199, | |
"4": 0.535311115, | |
"5": 0.526155018, | |
"6": 0.234408449 | |
} | |
}, { | |
"name": "720-759", | |
"values": { | |
"1": 0.545049517, | |
"2": 0.238328877, | |
"3": 0.133471737, | |
"4": 0.037855184, | |
"5": 0.022462245, | |
"6": 0.005850939 | |
} | |
}, { | |
"name": "760-799", | |
"values": { | |
"1": 0.242711679, | |
"2": 0.052358647, | |
"3": 0.013872764, | |
"4": 0.001577071, | |
"5": 0.00073054 | |
} | |
}, { | |
"name": " > 799", | |
"values": { | |
"1": 0.061959014, | |
"2": 0.011314837, | |
"3": 0.00171382, | |
"5": 0.00028495 | |
} | |
}], | |
"2013": [{ | |
"name": "< 680", | |
"values": { | |
"1": 0.023571756, | |
"2": 0.195151221, | |
"3": 0.310355648, | |
"4": 0.434717223, | |
"5": 0.461840072, | |
"6": 0.548137738 | |
} | |
}, { | |
"name": "680-719", | |
"values": { | |
"1": 0.382264916, | |
"2": 0.600494954, | |
"3": 0.56921187, | |
"4": 0.497816621, | |
"5": 0.492143478, | |
"6": 0.422219252 | |
} | |
}, { | |
"name": "720-759", | |
"values": { | |
"1": 0.400716441, | |
"2": 0.170888523, | |
"3": 0.105929433, | |
"4": 0.060732611, | |
"5": 0.040102649, | |
"6": 0.02732842 | |
} | |
}, { | |
"name": "760-799", | |
"values": { | |
"1": 0.151039722, | |
"2": 0.027613396, | |
"3": 0.012099796, | |
"4": 0.005199786, | |
"5": 0.005058964, | |
"6": 0.002209775 | |
} | |
}, { | |
"name": " > 799", | |
"values": { | |
"1": 0.042407165, | |
"2": 0.005851907, | |
"3": 0.002403253, | |
"4": 0.001533759, | |
"5": 0.000854837, | |
"6": 0.000104815 | |
} | |
}], | |
"2014": [{ | |
"name": "< 680", | |
"values": { | |
"1": 0.09100394, | |
"2": 0.260715312, | |
"3": 0.357812121, | |
"4": 0.428166968, | |
"5": 0.500877119, | |
"6": 0.582926869 | |
} | |
}, { | |
"name": "680-719", | |
"values": { | |
"1": 0.40991467, | |
"2": 0.512238814, | |
"3": 0.50450076, | |
"4": 0.492149704, | |
"5": 0.451578706, | |
"6": 0.38127527 | |
} | |
}, { | |
"name": "720-759", | |
"values": { | |
"1": 0.343033612, | |
"2": 0.181768549, | |
"3": 0.117484927, | |
"4": 0.072160131, | |
"5": 0.042532396, | |
"6": 0.030031709 | |
} | |
}, { | |
"name": "760-799", | |
"values": { | |
"1": 0.126384859, | |
"2": 0.037901088, | |
"3": 0.017083752, | |
"4": 0.006185321, | |
"5": 0.004293123, | |
"6": 0.004388759 | |
} | |
}, { | |
"name": " > 799", | |
"values": { | |
"1": 0.029662918, | |
"2": 0.007376236, | |
"3": 0.00311844, | |
"4": 0.001337875, | |
"5": 0.000718656, | |
"6": 0.001377393 | |
} | |
}], | |
"2015": [{ | |
"name": "< 680", | |
"values": { | |
"1": 0.070046383, | |
"2": 0.270228942, | |
"3": 0.382762751, | |
"4": 0.452456944, | |
"5": 0.481655394, | |
"6": 0.530177643 | |
} | |
}, { | |
"name": "680-719", | |
"values": { | |
"1": 0.454732716, | |
"2": 0.497872156, | |
"3": 0.487494901, | |
"4": 0.463295654, | |
"5": 0.446155064, | |
"6": 0.409546578 | |
} | |
}, { | |
"name": "720-759", | |
"values": { | |
"1": 0.314743806, | |
"2": 0.183511918, | |
"3": 0.109837217, | |
"4": 0.07301078, | |
"5": 0.063317006, | |
"6": 0.051229672 | |
} | |
}, { | |
"name": "760-799", | |
"values": { | |
"1": 0.123455357, | |
"2": 0.039320465, | |
"3": 0.01683621, | |
"4": 0.010105941, | |
"5": 0.007550441, | |
"6": 0.008328697 | |
} | |
}, { | |
"name": " > 799", | |
"values": { | |
"1": 0.037021739, | |
"2": 0.00906652, | |
"3": 0.003068921, | |
"4": 0.001130681, | |
"5": 0.001322096, | |
"6": 0.000717411 | |
} | |
}], | |
"2016": [{ | |
"name": "< 680", | |
"values": { | |
"1": 0.032102761, | |
"2": 0.282392528, | |
"3": 0.387682619, | |
"4": 0.452072231, | |
"5": 0.485884013, | |
"6": 0.516687305 | |
} | |
}, { | |
"name": "680-719", | |
"values": { | |
"1": 0.451597134, | |
"2": 0.489041153, | |
"3": 0.483377285, | |
"4": 0.457223451, | |
"5": 0.441015531, | |
"6": 0.419706296 | |
} | |
}, { | |
"name": "720-759", | |
"values": { | |
"1": 0.332516374, | |
"2": 0.179264739, | |
"3": 0.108623205, | |
"4": 0.075675392, | |
"5": 0.06315108, | |
"6": 0.05555666 | |
} | |
}, { | |
"name": "760-799", | |
"values": { | |
"1": 0.140067131, | |
"2": 0.039911557, | |
"3": 0.017053532, | |
"4": 0.013692641, | |
"5": 0.008405617, | |
"6": 0.007302824 | |
} | |
}, { | |
"name": " > 799", | |
"values": { | |
"1": 0.043716599, | |
"2": 0.009390023, | |
"3": 0.003263359, | |
"4": 0.001336285, | |
"5": 0.00154376, | |
"6": 0.000746914 | |
} | |
}] | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<!DOCTYPE html> | |
<html lang="en"> | |
<head> | |
<meta charset="utf-8"> | |
<style> | |
body { | |
font-family: -apple-system, system-ui, BlinkMacSystemFont, "Segoe UI", "Roboto", "Helvetica Neue", Arial, sans-serif; | |
} | |
label { | |
margin-right: 1rem; | |
} | |
fieldset { | |
border: none; | |
} | |
legend { | |
font-weight: bold; | |
} | |
</style> | |
</head> | |
<body> | |
<h1></h1> | |
<svg width="960" height="500"/> | |
<div class="controls"></div> | |
<script src="//d3js.org/d3.v4.min.js"></script> | |
<script src="//cdn.jsdelivr.net/lodash/4.17.4/lodash.min.js" charset="utf-8"></script> | |
<script src=".script-compiled.js"></script> | |
<script> | |
// change frame height | |
d3.select(self.frameElement).style('height', '660px'); | |
</script> | |
</body> | |
</html> |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/*eslint no-undef: 0*/ | |
function createChart (svg, data) { | |
const colors = ['#98abc5', '#8a89a6', '#7b6888', '#6b486b', '#a05d56', '#d0743c', '#ff8c00'] | |
svg = d3.select(svg) | |
const margin = {top: 20, right: 20, bottom: 30, left: 40} | |
const width = 960 - margin.left - margin.right | |
const height = 500 - margin.top - margin.bottom | |
const g = svg.append('g').attr('transform', 'translate(' + margin.left + ',' + margin.top + ')') | |
var x0 = d3.scaleBand() | |
.rangeRound([0, width]) | |
.paddingInner(0.1) | |
var x1 = d3.scaleBand() | |
.padding(0.05) | |
var y = d3.scaleLinear() | |
.rangeRound([height, 0]) | |
var z = d3.scaleOrdinal() | |
.range(colors) | |
// check each subset of data for possible sections, since not all subsets have every possible section. | |
let nameKeys = data[Object.keys(data)[0]].map(obj =>obj.name) | |
let valueKeys = ["1", "2", "3", "4", "5", "6"] | |
//fill in empty data entries | |
Object.keys(data).forEach((d)=>{ | |
data[d].forEach(section=>{ | |
valueKeys.forEach(k=>{ | |
if (section.values[k] === undefined) section.values[k] = 0 | |
}) | |
}) | |
}) | |
x0.domain(nameKeys) | |
x1.domain(valueKeys).rangeRound([0, x0.bandwidth()]) | |
const barContainer = g.append('g') | |
const xAxis = g.append('g') | |
.attr('class', 'axis') | |
.attr('transform', 'translate(0,' + height + ')') | |
.call(d3.axisBottom(x0)) | |
const yAxis = g.append('g') | |
.attr('class', 'axis') | |
yAxis | |
.append('text') | |
.attr('x', 2) | |
.attr('y', y(y.ticks().pop()) + 0.5) | |
.attr('dy', '0.32em') | |
.attr('font-weight', 'bold') | |
.attr('text-anchor', 'start') | |
.text('Prop Value') | |
var legend = g.append('g') | |
.attr('font-size', 10) | |
.attr('text-anchor', 'end') | |
legend.append('text') | |
.text('Loan Grade') | |
.attr('x', width - 19) | |
.style('font-weight', 'bold') | |
.attr('dy', -10) | |
.attr('dx', 20) | |
var legendEnter = legend | |
.selectAll('g') | |
.data(valueKeys) | |
.enter().append('g') | |
.attr('transform', function (d, i) { return 'translate(0,' + i * 20 + ')' }) | |
legendEnter.append('rect') | |
.attr('x', width - 19) | |
.attr('width', 19) | |
.attr('height', 19) | |
.attr('fill', z) | |
legendEnter.append('text') | |
.attr('x', width - 24) | |
.attr('y', 9.5) | |
.attr('dy', '0.32em') | |
.text(d => d) | |
const stack = d3.stack() | |
.keys(valueKeys) | |
// updates both the year + the chart type (group or stacked) | |
function updateChart (data, chartType='group') { | |
// ======================================================== | |
// show grouped view | |
// ======================================================== | |
if (chartType === 'group'){ | |
//find max value of a section | |
const maxValue = d3.max(data.map((d) => Object.values(d.values)).reduce((a, b) => a.concat(b), [])) | |
y.domain([0, maxValue]).nice() | |
yAxis.transition() | |
.call(d3.axisLeft(y)) | |
const barsWithData = barContainer | |
.selectAll('g') | |
.data(data) | |
barsWithData.exit().remove() | |
const bars = barsWithData | |
.enter() | |
.append('g') | |
.attr('transform', function (d) { return 'translate(' + x0(d.name) + ',0)' }) | |
.merge(barsWithData) | |
.selectAll('rect') | |
.data(function (d) { | |
return Object.keys(d.values).map(k => ({ key: k, value: d.values[k] })) | |
}) | |
bars.exit().transition().style('opacity', 0).remove() | |
bars | |
.enter() | |
.append('rect') | |
.attr('fill', function (d) { | |
return z(d.key) | |
}) | |
// start y at height (0) so animation in looks like bars are growing upwards | |
.attr('y', height) | |
.merge(bars) | |
.transition() | |
.attr('width', x1.bandwidth()) | |
.attr('x', function (d) { return x1(d.key) }) | |
.attr('y', d => y(d.value)) | |
.attr('height', d => height - y(d.value)) | |
} | |
// ======================================================== | |
// show stacked view | |
// ======================================================== | |
else if (chartType === 'stack'){ | |
//find max value of a section | |
const maxValue = d3.max( | |
data.map((d) => Object.values(d.values)) | |
.map((valueArray)=>{ | |
return valueArray.reduce((a,b)=> a+ b) | |
}) | |
) | |
y.domain([0, maxValue]).nice() | |
yAxis.transition() | |
.call(d3.axisLeft(y)) | |
//add data for missing bars | |
const seriesFlipped = stack(data.map(d=>{ | |
const defaultData = {} | |
valueKeys.forEach(k=> defaultData[k] = 0) | |
return Object.assign(defaultData, d.values) | |
})) | |
const series = [] | |
//need to reorient the series | |
//we want a list of groups, not a list of rects from each level | |
seriesFlipped[0].forEach((col, i)=>{ | |
const arr = [] | |
seriesFlipped.forEach((row, index2)=>{ | |
//mimic the key from the grouped data format | |
row[i].key = index2 + 1 + '' | |
arr.push(row[i]) | |
}) | |
series.push(arr) | |
}) | |
const barSections = barContainer | |
.selectAll('g') | |
.data(series) | |
const bars = barSections | |
.enter() | |
.append('g') | |
.merge(barSections) | |
.attr('transform', (d,i)=> {console.log(x0(nameKeys[i])); return 'translate(' + x0(nameKeys[i]) + ',0)'} ) | |
.selectAll('rect') | |
.data(d=>d, (d)=> d.key) | |
const enterBars = bars.enter().append('rect') | |
.attr('fill', (d)=> z(d.key)) | |
bars.exit().transition().style('opacity', 0).remove() | |
enterBars | |
.merge(bars) | |
.transition() | |
.delay((d,i)=> i * 50) | |
.attr('width', x0.bandwidth()) | |
.attr("y", function(d) {return y(d[1]) }) | |
.attr("x", 0) | |
.attr("height", function(d) { return y(d[0]) - y(d[1]) }) | |
} | |
} | |
return { | |
updateChart | |
} | |
} | |
d3.json('./data.json', function(error, data){ | |
//start with the first year selected | |
const chart = createChart(document.querySelector('svg'), data) | |
// append the input controls | |
const fieldset1 = d3.select('.controls').append('fieldset') | |
fieldset1.append('legend').text('Year') | |
Object.keys(data).forEach((year, index )=>{ | |
const label = fieldset1.append('label') | |
label | |
.append('input') | |
.attr('type', 'radio') | |
.attr('name', 'year') | |
.attr('value', year) | |
.attr('checked', function(){ | |
if (index === 0) return true | |
return null | |
}) | |
label.append('span') | |
.text(year) | |
label.on('click', function(){ | |
chart.updateChart(data[year], document.querySelector('input[name="graphType"]:checked').value) | |
}) | |
}) | |
const fieldset2 = d3.select('.controls').append('fieldset') | |
const types = ['group', 'stack'] | |
fieldset2.append('legend').text('Graph Layout') | |
types.forEach((graphType, index)=>{ | |
const label = fieldset2.append('label') | |
label.append('input') | |
.attr('type', 'radio') | |
.attr('name', 'graphType') | |
.attr('value', graphType) | |
.attr('checked', function(){ | |
if (index === 0) return true | |
return null | |
}) | |
.on('click', ()=>{ | |
chart.updateChart(data[document.querySelector('input[name="year"]:checked').value], graphType) | |
}) | |
label.append('span') | |
.text(graphType) | |
}) | |
// render initial chart | |
chart.updateChart(data[Object.keys(data)[0]]) | |
}) |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment