Built with blockbuilder.org
forked from jonsadka's block: Individual to stacked area charts
license: mit |
Built with blockbuilder.org
forked from jonsadka's block: Individual to stacked area charts
<!DOCTYPE html> | |
<head> | |
<meta charset="utf-8"> | |
<script src="https://d3js.org/d3.v4.min.js"></script> | |
<script src="https://d3js.org/d3-scale-chromatic.v1.min.js"></script> | |
<style> | |
body { margin:0;position:fixed;top:0;right:0;bottom:0;left:0; } | |
.x-axis .tick line { | |
stroke: #EFEFEF; | |
} | |
path { | |
animation: dash 2s linear infinite; | |
} | |
@keyframes dash { | |
to { | |
stroke-dashoffset: -16; | |
} | |
} | |
</style> | |
</head> | |
<body> | |
<svg width="960" height="500"></svg> | |
<script> | |
function generateRandomData(years){ | |
var randomData = []; | |
for (var i = 0; i < years; i++){ | |
randomData.push({type: 'local', year: 2000 + i, taxRate: _randomLocalData()}); | |
randomData.push({type: 'state', year: 2000 + i, taxRate: _randomStateData()}); | |
randomData.push({type: 'federal', year: 2000 + i, taxRate: _randomFederalData()}); | |
} | |
return randomData; | |
function _randomLocalData(){return Math.min(0.04, Math.random()/10);} | |
function _randomStateData(){return Math.min(0.11, Math.random()/8);} | |
function _randomFederalData(){return Math.min(0.25, Math.random()/4);} | |
} | |
</script> | |
<script> | |
////// COMPOSE DATA | |
var taxData = generateRandomData(20, 100000); | |
var taxDataByYear = d3.nest() | |
.key(function(d){return d.year;}) | |
.entries(taxData); | |
var taxDataByType = d3.nest() | |
.key(function(d){return d.type;}) | |
.entries(taxData); | |
var taxDataForStack = taxDataByYear.reduce(function(taxData, data){ | |
var newData = {year: data.key}; | |
data.values.forEach(function(d){newData[d.type] = d.taxRate;}); | |
taxData.push(newData); | |
return taxData; | |
}, []) | |
var years = taxDataByYear.map(function(d){return +d.key;}) | |
var localTaxData = taxDataByType[0].values; | |
var stateTaxData = taxDataByType[1].values; | |
var federalTaxData = taxDataByType[2].values; | |
////// SETUP CONTAINERS | |
var svg = d3.select('svg') | |
var margin = {top: 40, right: 40, bottom: 50, left: 60}; | |
var height = +svg.attr('height') - margin.top - margin.bottom; | |
var width = +svg.attr('width') - margin.left - margin.right; | |
var chartPadding = 15; | |
var chartHeight = height / taxDataByType.length - 15; | |
////// COMPOSE GENERATORS | |
var generators = { | |
'shared': { | |
color: d3.scaleOrdinal(d3.schemePastel1).domain(['local', 'state', 'federal']), | |
stack: d3.stack().keys(['local', 'state', 'federal']).order(d3.stackOrderDescending).offset(d3.stackOffsetNone), | |
x: d3.scaleLinear().domain([d3.min(years), d3.max(years)]).range([0, width]), | |
y: d3.scaleLinear().domain([d3.max(federalTaxData, function(d){return d.taxRate}), 0]).range([0, height]) | |
}, | |
'local': {}, | |
'state': {}, | |
'federal': {y: d3.scaleLinear().domain([d3.max(federalTaxData, function(d){return d.taxRate}), 0]).range([0, chartHeight])} | |
}; | |
var stackTaxData = generators.shared.stack(taxDataForStack); | |
var maxStackData = d3.max(stackTaxData, function(d){return d3.max(d, function(d){return d[1];});}); | |
generators.shared.xAxis = d3.axisBottom(generators.shared.x).tickValues(years).tickFormat(d3.format('.4r')).tickSizeInner(-height); | |
generators.shared.y = d3.scaleLinear().domain([maxStackData, 0]).range([0, height]); | |
generators.shared.yAxis = d3.axisLeft(generators.shared.y).tickFormat(d3.format('.2f')); | |
generators.shared.area = d3.area().x(function(d){return generators.shared.x(d.data.year);}) | |
.y0(function(d){return generators.shared.y(d[0]);}).y1(function(d){return generators.shared.y(d[1]);}) | |
.curve(d3.curveMonotoneX) | |
generators.local.area = d3.area(localTaxData).x(function(d){return generators.shared.x(d.year);}) | |
.y0(function(d){return generators.federal.y(0);}).y1(function(d){return generators.federal.y(d.taxRate);}) | |
.curve(d3.curveMonotoneX); | |
generators.state.area = d3.area(stateTaxData).x(function(d){return generators.shared.x(d.year);}) | |
.y0(function(d){return generators.federal.y(0);}).y1(function(d){return generators.federal.y(d.taxRate);}) | |
.curve(d3.curveMonotoneX); | |
generators.federal.yAxis = d3.axisLeft(generators.federal.y).ticks(4).tickFormat(d3.format('.2f')); | |
generators.federal.area = d3.area(federalTaxData).x(function(d){return generators.shared.x(d.year);}) | |
.y0(function(d){return generators.federal.y(0);}).y1(function(d){return generators.federal.y(d.taxRate);}) | |
.curve(d3.curveMonotoneX); | |
////// MAKE IT ALIVE! | |
var grouping = svg.append('g') | |
.attr('transform', 'translate(' + margin.left + ',' + margin.top + ')'); | |
grouping.append('g') | |
.call(generators.shared.xAxis) | |
.attr('transform', 'translate(0,' + height + ')') | |
.attr('class', 'x-axis'); | |
containers = grouping.selectAll('.containers').data(taxDataByType).enter() | |
.append('g') | |
.attr('transform', function(d, i){ return 'translate(0, ' + ((chartHeight + chartPadding) * i) + ')'; }) | |
.attr('class', 'containers') | |
containers.append('g') | |
.attr('class', 'y-axis') | |
.call(generators.federal.yAxis) | |
containers.append('path') | |
.attr('d', function(d){return generators[d.key].area(d.values);}) | |
.attr('class', 'stack-container') | |
.attr('stroke', 'gray') | |
.attr('stroke-dasharray', '8 8') | |
.attr('fill', function(d){return generators.shared.color(d.key)}) | |
var stacked = false; | |
setInterval(function(){ | |
var selection = d3.selectAll('.containers') | |
if (stacked){ | |
selection.data(taxDataByType); | |
selection | |
.transition().duration(1000) | |
.attr('transform', function(d, i){ return 'translate(0, ' + ((chartHeight + chartPadding) * i) + ')'; }) | |
.select('.y-axis') | |
.call(generators.federal.yAxis); | |
selection.select('.stack-container') | |
.transition().duration(1000) | |
.attr('d', function(d){return generators[d.key].area(d.values);}); | |
} else { | |
selection.data(stackTaxData); | |
selection | |
.transition().duration(1000) | |
.attr('transform', 'translate(0,0)') | |
.select('.y-axis') | |
.call(generators.shared.yAxis) | |
selection.select('.stack-container') | |
.transition().duration(1000) | |
.attr('d', generators.shared.area); | |
} | |
stacked = !stacked; | |
}, 2000) | |
</script> | |
</body> |