Built with blockbuilder.org
forked from sxywu's block: Chase Data: Bar Chart
Built with blockbuilder.org
forked from sxywu's block: Chase Data: Bar Chart
| <!DOCTYPE html> | |
| <head> | |
| <meta charset="utf-8"> | |
| <script src="https://npmcdn.com/babel-core@5.8.34/browser.min.js"></script> | |
| <script src="https://cdnjs.cloudflare.com/ajax/libs/d3/3.5.5/d3.min.js"></script> | |
| <script src='https://cdnjs.cloudflare.com/ajax/libs/lodash.js/4.11.2/lodash.js'></script> | |
| <style> | |
| body { margin:0;position:fixed;top:0;right:0;bottom:0;left:0; } | |
| .axis path, | |
| .axis line { | |
| fill: none; | |
| stroke: #000; | |
| shape-rendering: crispEdges; | |
| } | |
| </style> | |
| </head> | |
| <body> | |
| <input id="loadfile" type="file" multiple /> | |
| <svg></svg> | |
| <div id='data'></div> | |
| <script type="text/babel"> | |
| /************** | |
| * This section modified from @enjalot's block: | |
| http://bl.ocks.org/enjalot/63d06e2ccadad0cb30dc5f920efd1cdf | |
| ***************/ | |
| d3.select("#loadfile").node() | |
| .addEventListener('change', loadFile, false); | |
| function loadFile(evt) { | |
| evt.stopPropagation(); | |
| evt.preventDefault(); | |
| var files; | |
| if(evt.dataTransfer) { | |
| files = evt.dataTransfer.files; | |
| } else { | |
| files = evt.target.files; | |
| } | |
| var i = 0; | |
| var reader = new FileReader(); | |
| reader.onload = function(e) { | |
| parseData(e.target.result); | |
| // start the next | |
| i += 1; | |
| files[i] && reader.readAsText(files[i]); | |
| } | |
| reader.readAsText(files[i]); | |
| } | |
| var allTransactions = []; | |
| function parseData(result) { | |
| var transactions = d3.csv.parse(result); | |
| // go through each transaction | |
| _.each(transactions, transaction => { | |
| if (transaction['Type'] === 'Sale') { | |
| allTransactions.push({ | |
| date: new Date(transaction['Trans Date']), | |
| amount: -1 * parseFloat(transaction['Amount']), | |
| title: transaction['Description'], | |
| }); | |
| } | |
| }); | |
| updateGraph(allTransactions); | |
| } | |
| // draw bar chart | |
| var width = 900; | |
| var height = 500; | |
| var barWidth = 1; | |
| var padding = {top: 40, left: 20}; | |
| var linearGradient = d3.select('svg').append('defs') | |
| .append("linearGradient") | |
| .attr("id", "linear-gradient") | |
| .attr({gradientUnits: 'userSpaceOnUse', | |
| x1: 0, | |
| y1: 0, | |
| x2: 0, | |
| y2: height - padding.top}); | |
| var svg = d3.select('svg') | |
| .attr({width, height}) | |
| .append('g'); | |
| var timeFormat = d3.time.format('%a %b %e'); | |
| // do da linear gradient | |
| linearGradient.append("stop") | |
| .attr("offset", 0) | |
| .attr("stop-color", "#017351"); // start at red | |
| /** | |
| linearGradient.append("stop") | |
| .attr("offset", '50%') | |
| .attr("stop-color", "#03c383"); **/ | |
| linearGradient.append("stop") | |
| .attr("offset", '100%') | |
| .attr("stop-color", "#aad962"); // end at green | |
| // initialize scales and axis | |
| var colorScale = d3.scale.linear() | |
| .range(["#aad962", "#03c383", "#017351"]); | |
| var timeScale = d3.time.scale() | |
| .range([0, width - padding.left]); | |
| var xAxis = d3.svg.axis() | |
| .orient('bottom') | |
| .scale(timeScale); | |
| var heightScale = d3.scale.linear() | |
| .range([0, height - padding.top]); | |
| var yAxis = d3.svg.axis() | |
| .orient('left') | |
| .scale(heightScale); | |
| // initialize the containers | |
| var barsG = svg.append('g') | |
| .attr('transform', 'translate(' + padding.left + ',0)'); | |
| var xAxisG = svg.append('g') | |
| .classed('axis', true) | |
| .attr('transform', | |
| 'translate(' + padding.left + ',' + (height - padding.top) + ')'); | |
| var yAxisG = svg.append('g') | |
| .classed('axis', true) | |
| .attr('transform', 'translate(' + padding.left + ',0)'); | |
| function updateGraph(data) { | |
| // first group the data into days | |
| data = _.chain(data) | |
| .groupBy(d => d.date) | |
| .map((transactions) => { | |
| return { | |
| date: transactions[0].date, | |
| transactions: _.sortBy(transactions, t => t.amount), | |
| total: _.reduce(transactions, (sum, t) => { | |
| return t.amount ? sum + t.amount : sum; | |
| }, 0), | |
| }; | |
| }).sortBy(d => d.date).value(); | |
| barWidth = Math.floor((width - padding.left) / data.length) - 2; | |
| // update the scales | |
| timeScale.domain([data[0].date, _.last(data).date]); | |
| var maxTotal = d3.max(data, d => d.total); | |
| heightScale.domain([0, maxTotal]); | |
| colorScale.domain([0, maxTotal / 2, maxTotal]); | |
| xAxisG.call(xAxis); | |
| // yAxisG.call(yAxis); | |
| var bars = barsG.selectAll('g') | |
| .data(data, d => d.date); | |
| bars.enter().append('g'); | |
| bars.exit().remove(); | |
| bars.attr('transform', d => { | |
| return 'translate(' + timeScale(d.date) + ',' + | |
| (height - padding.top - heightScale(d.total)) + ')'; | |
| }); | |
| var bar = bars.selectAll('rect') | |
| .data(d => d.transactions); | |
| bar.enter().append('rect'); | |
| bar.exit().remove(); | |
| var allY = 0; | |
| bar.attr({ | |
| x: -barWidth / 2, | |
| y: (d, i) => { | |
| var y = allY; | |
| allY += heightScale(d.amount); | |
| if (!i) { | |
| // if this is the first rect | |
| y = 0; | |
| allY = heightScale(d.amount); | |
| } | |
| return y; | |
| }, | |
| width: barWidth, | |
| height: d => heightScale(d.amount), | |
| fill: d => colorScale(d.amount), | |
| 'fill-opacity': 0.5, | |
| stroke: d => colorScale(d.amount), | |
| cursor: 'pointer', | |
| }).on('mouseover', d => { | |
| var html = d.title + ' '; | |
| html += '(' + timeFormat(d.date) + '): '; | |
| html += '<b>$' + d.amount + '</b><br>'; | |
| d3.select('#data') | |
| .html(html); | |
| }); | |
| // move the group to the correct place | |
| /** | |
| bars | |
| .attr({ | |
| width: barWidth, | |
| height: d => heightScale(d.amount), | |
| fill: d => colorScale(d.amount), | |
| stroke: '#fff', | |
| }) | |
| ; | |
| **/ | |
| } | |
| </script> | |
| </body> |