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> |