forked from SpaceActuary's block: Bar Chart Transitions - Positive to Negative
forked from SpaceActuary's block: Trapezoid Charts
forked from SpaceActuary's block: Bar Chart Transitions - Positive to Negative
forked from SpaceActuary's block: Trapezoid Charts
| <!DOCTYPE html> | |
| <meta charset="utf-8"> | |
| <style> | |
| body { | |
| margin: 0; | |
| position: fixed; | |
| top: 0; right: 0; bottom: 0; left: 0; | |
| font-family: Arial, Helvetica, sans-serif | |
| } | |
| svg { | |
| width: 100%; | |
| height: 100%; | |
| } | |
| .axis { | |
| /*stroke: lightgrey;*/ | |
| shape-rendering: crispEdges; | |
| } | |
| .axis path { | |
| fill: none; | |
| stroke: lightgrey; | |
| } | |
| .x.axis line { | |
| display: none; | |
| } | |
| .y.axis line { | |
| fill: none; | |
| stroke: lightgrey; | |
| } | |
| path.outline { | |
| fill: none; | |
| stroke: #000000; | |
| stroke-width: 1.5; | |
| } | |
| line.total { | |
| stroke-dasharray: 5,5; | |
| } | |
| </style> | |
| <body> | |
| <button onClick="randomize();">Randomize</button> | |
| <button onClick="toggleVariance();">Toggle Variance-Only</button> | |
| <script src="http://d3js.org/d3.v3.min.js"></script> | |
| <script> | |
| var years = d3.range(2007, 2016, 1).map(function(d){ return {year: d};}); | |
| var varianceToggle = false; | |
| var randomizeData = function(d){ | |
| return { | |
| year: d.year, | |
| prior: (Math.random() * 6) + 3, | |
| actual: (Math.random() * 5) + 4, | |
| expected: (Math.random() * 5) + 4, | |
| }; | |
| }; | |
| data = years.map(randomizeData); | |
| var margin = {top: 30, right: 20, bottom: 50, left: 40}, | |
| width = 960 - margin.left - margin.right; | |
| height = 500 - margin.top - margin.bottom; | |
| t = 1000, // transition time | |
| b = width / data.length // block width | |
| var x = d3.scale.ordinal() | |
| .rangeRoundBands([0, width], .1) | |
| .domain(data.map(function(d) { return d.year; })); | |
| var y = d3.scale.linear() | |
| .range([height, 0]) | |
| .domain([0, d3.max(data, function(d){ | |
| return d3.max([d.prior, d.actual, d.expected]) | |
| })]) | |
| .nice(); | |
| var xAxis = d3.svg.axis().scale(x).orient("bottom"); | |
| var yAxis = d3.svg.axis().scale(y).orient("left"); | |
| var svg = d3.select("body").append("svg") | |
| .attr("width", width + margin.left + margin.right) | |
| .attr("height", height + margin.top + margin.bottom) | |
| .append("g") | |
| .attr("transform", "translate(" + margin.left + "," + margin.top + ")"); | |
| svg.append("g") | |
| .attr("class", "y axis") | |
| .attr("transform", "translate(0,0)") | |
| .call(yAxis); | |
| svg.append("g") | |
| .attr("class", "x axis") | |
| .attr("transform", "translate(0," + height + ")") | |
| .call(xAxis); | |
| svg.append("line") | |
| .attr("class","total") | |
| //This is the accessor function we talked about above | |
| var lineFunction = d3.svg.line() | |
| .x(function(d) { return d.x; }) | |
| .y(function(d) { return d.y; }) | |
| .interpolate("linear"); | |
| var drawTrapezoid = function(d, i){ | |
| var data; | |
| if(varianceToggle){ | |
| data = [ | |
| {x: x(d.year), y: y(0)}, | |
| {x: x(d.year), y: y(0)}, | |
| {x: x(d.year) + x.rangeBand(), y: y(d.actual - d.expected)}, | |
| {x: x(d.year) + x.rangeBand(), y: y(0)}, | |
| {x: x(d.year), y: y(0)} | |
| ] | |
| } else { | |
| data = [ | |
| {x: x(d.year), y: y(0) }, | |
| {x: x(d.year), y: y(d.prior) }, | |
| {x: x(d.year) + x.rangeBand(), y: y(d.actual) }, | |
| {x: x(d.year) + x.rangeBand(), y: y(0) }, | |
| {x: x(d.year), y: y(0) } | |
| ] | |
| } | |
| return lineFunction(data); | |
| } | |
| var drawVariance = function(d, i){ | |
| var data; | |
| if(varianceToggle){ | |
| data = [ | |
| {x: x(d.year), y: y(0)}, | |
| {x: x(d.year) + x.rangeBand(), y: y(d.actual - d.expected)}, | |
| {x: x(d.year) + x.rangeBand(), y: y(0)} | |
| ] | |
| } else { | |
| data = [ | |
| {x: x(d.year), y: y(d.prior)}, | |
| {x: x(d.year) + x.rangeBand(), y: y(d.actual)}, | |
| {x: x(d.year) + x.rangeBand(), y: y(d.expected)} | |
| ] | |
| } | |
| return lineFunction(data); | |
| } | |
| var redraw = function(data){ | |
| if (varianceToggle){ | |
| // get the min / max variance | |
| y.domain(d3.extent(data, function(d){ | |
| return d.actual - d.expected; | |
| })).nice();; | |
| } else { | |
| // domain = [0, largest value] | |
| y.domain([0, d3.max(data, function(d){ | |
| return d3.max([d.prior, d.actual, d.expected]) | |
| })]).nice(); | |
| } | |
| var trapezoids = svg.selectAll("path.bars").data(data) | |
| trapezoids.enter().append("path") | |
| .attr("class","bars") | |
| .attr("fill", "#c8c8c8"); | |
| trapezoids.transition().duration(1000) | |
| .attr("d", drawTrapezoid) | |
| var variances = svg.selectAll("path.variance").data(data) | |
| variances.enter().append("path") | |
| .attr("class","variance"); | |
| variances.transition().duration(1000) | |
| .attr("d", drawVariance) | |
| .attr("fill", function(d){ | |
| return d.actual > d.expected ? "crimson" : "seagreen"; | |
| }); | |
| var outline = svg.selectAll("path.outline").data(data) | |
| outline.enter().append("path") | |
| .attr("class","outline"); | |
| outline.transition().duration(1000) | |
| .attr("d", drawTrapezoid); | |
| // redraw y-axis | |
| svg.selectAll("g.y.axis") | |
| .transition() | |
| .duration(1000) | |
| .call(yAxis); | |
| } | |
| redraw(data); | |
| var randomize = function(){ | |
| data = data.map(randomizeData) | |
| redraw(data); | |
| } | |
| var toggleVariance = function(){ | |
| varianceToggle = !varianceToggle; | |
| redraw(data); | |
| } | |
| </script> | |
| </body> | |
| </html> |