- this slope chart will transition from one set of data to another on click of the update button.
To do:
- enable going back to original data on re-click
- color gradient based on difference between y1,y2
| <!DOCTYPE html> | |
| <head> | |
| <meta charset="utf-8"> | |
| <script src="https://cdnjs.cloudflare.com/ajax/libs/d3/3.5.5/d3.min.js"></script> | |
| <style> | |
| * { | |
| margin: 0; | |
| padding: 0; | |
| } | |
| #container { | |
| width: 800px; | |
| margin: 25px auto 25px auto; | |
| padding: 50px 50px 50px 50px; | |
| background-color: white; | |
| } | |
| .chartContainer { | |
| display: inline-block; | |
| width: 99%; | |
| } | |
| body { | |
| margin:0;position:fixed;top:0;right:0;bottom:0;left:0; | |
| font-family: 'Helvetica Neue', Helvetica, Arial, sans-serif; | |
| font-size: 20px; | |
| font-style: bold; | |
| } | |
| h1 { | |
| margin-bottom: 25px; | |
| font-family: 'Helvetica Neue', Helvetica, Arial, sans-serif; | |
| font-size: 28px; | |
| font-style: bold; | |
| } | |
| svg { | |
| background-color: white; | |
| } | |
| .axis path, | |
| .axis line { | |
| fill: none; | |
| stroke: black; | |
| shape-rendering: crispEdges; | |
| } | |
| .axis text { | |
| font-family: sans-serif; | |
| font-size: 11px; | |
| } | |
| circle { | |
| stroke-width: 4; | |
| fill: white; | |
| } | |
| circle.circles2b { | |
| stroke: white; | |
| stroke-width: 3; | |
| fill: white; | |
| } | |
| circle.circles1b { | |
| stroke: white; | |
| stroke-width: 3; | |
| fill: white; | |
| } | |
| .axis path, | |
| .axis line { | |
| fill: none; | |
| stroke: black; | |
| stroke-width: 2; | |
| shape-rendering: crispEdges; | |
| } | |
| .axis text { | |
| font-family: 'Helvetica Neue', Helvetica, Arial, sans-serif; | |
| font-size: 18px; | |
| font-style: bold; | |
| } | |
| g.lines.highlight { | |
| opacity: 0.95; | |
| } | |
| g.lines.highlight circle { | |
| fill: #ff0000; | |
| stroke: #ff0000; | |
| } | |
| g.lines.highlight line { | |
| stroke: #ff0000; | |
| } | |
| #option { | |
| margin: 10px; | |
| } | |
| </style> | |
| </head> | |
| <body> | |
| <div id="container"> | |
| <h1>Slope Chart</h1> | |
| <div id="option"> | |
| <input name="updateButton" | |
| type="button" | |
| value="Update" | |
| onclick="updateData()" /> | |
| </div> | |
| <div class="chartContainer" id="slopeChartContainer"> | |
| </div> | |
| </div> | |
| <script> | |
| //Width, height, padding | |
| var w = 400; | |
| var h = 600; | |
| var padding = 40; | |
| //data | |
| var dataset = [{"first":3.1,"second":5,"group":"up","delta":-1.9,"firstpct":0.1476,"secondpct":0.2381},{"first":5.3,"second":6,"group":"up","delta":-0.7,"firstpct":0.3533,"secondpct":0.4},{"first":1.7,"second":3.2,"group":"up","delta":-1.5,"firstpct":0.0944,"secondpct":0.1778},{"first":3.3,"second":7.3,"group":"up","delta":-4,"firstpct":0.1833,"secondpct":0.4056},{"first":2.4,"second":5.4,"group":"up","delta":-3,"firstpct":0.1043,"secondpct":0.2348},{"first":3.6,"second":8.5,"group":"up","delta":-4.9,"firstpct":0.2118,"secondpct":0.5},{"first":4.3,"second":9.1,"group":"up","delta":-4.8,"firstpct":0.1955,"secondpct":0.4136},{"first":1.1,"second":11.1,"group":"up","delta":-10,"firstpct":0.0458,"secondpct":0.4625},{"first":5.2,"second":15.7,"group":"up","delta":-10.5,"firstpct":0.208,"secondpct":0.628},{"first":4.9,"second":4.5,"group":"down","delta":0.4,"firstpct":0.3267,"secondpct":0.3},{"first":7,"second":2.6,"group":"down","delta":4.4,"firstpct":0.3043,"secondpct":0.113},{"first":3,"second":1.2,"group":"down","delta":1.8,"firstpct":0.1667,"secondpct":0.0667}] ; | |
| //Set button state | |
| //Default action for button will be to plot by Frequency | |
| //Get values for plotting | |
| var mydataset = []; // store their names within a local array | |
| for(var i = 0; i < dataset.length; i++){ | |
| mydataset.push( | |
| [dataset[i].first, dataset[i].second, dataset[i].delta] | |
| ) | |
| } | |
| //Find max values & Create scale functions | |
| var xMax = d3.max(mydataset, function(d) { return d[0]; }) | |
| var yMax = d3.max(mydataset, function(d) { return d[1]; }) | |
| var max = Math.max(yMax, xMax); // highest value in dataset | |
| var yScale = d3.scale.linear() | |
| .domain([0, max]) | |
| .range([h - padding, padding]) | |
| .nice() | |
| ; | |
| var rad = 6 // radius of circle | |
| var radback = rad * 1.3 | |
| //Configure y axis | |
| var yAxis = d3.svg.axis() | |
| .scale(yScale) | |
| .orient("left") | |
| .ticks(6) | |
| ; | |
| //Create SVG element | |
| var svg = d3.select("#slopeChartContainer") | |
| .append("svg") | |
| .attr("width", w) | |
| .attr("height", h); | |
| //Create groups | |
| var groups = svg.selectAll("g") | |
| .data(mydataset) // bind data to the group | |
| .enter() | |
| .append("g") | |
| .attr("class", "lines") // give each 'g' a bar class | |
| // .attr("transform", function(d, i) {return "translate(" + yScale(i) + ",0)";}) | |
| .on("mouseover", function(d) {d3.select(this).classed("highlight", true);}) | |
| .on("mouseout", function() {d3.select(this).classed("highlight", false);}); | |
| // join paths | |
| groups.append("line") | |
| .attr("x1", (w/4)) | |
| .attr("x2", (w/1.4)) | |
| .attr("y1", function(d) {return yScale(d[0])}) | |
| .attr("y2", function(d) {return yScale(d[1])}) | |
| .style("stroke-width", 4) | |
| .attr("stroke", function(d) { | |
| if (d[2] <= -10) {return "#000c60";} | |
| else if (d[2] >=0) {return "#569ffe";} | |
| else {return "#001bea";} | |
| }) | |
| .style("opacity", 1) | |
| ; | |
| //Create first background circles | |
| groups.append("circle") | |
| .attr("class", "circles1b") | |
| .attr("r", radback) | |
| .attr("cx", (w/4)) | |
| .attr("cy", function(d) {return yScale(d[0])}) | |
| ; | |
| //Create first circles | |
| groups.append("circle") | |
| .attr("class", "circle1") | |
| .attr("r", rad) | |
| .attr("cx", (w/4)) | |
| .attr("cy", function(d) {return yScale(d[0])}) | |
| .attr("stroke", function(d) { | |
| if (d[2] <= -10) {return "#000c60";} | |
| else if (d[2] >=0) {return "#569ffe";} | |
| else {return "#001bea";} | |
| }) | |
| .style("opacity", 1) | |
| ; | |
| //Create second background circles | |
| groups.append("circle") | |
| .attr("class", "circles2b") | |
| .attr("r", radback) | |
| .attr("cx", (w/1.4)) | |
| .attr("cy", function(d) {return yScale(d[1])}) | |
| ; | |
| //Create second circles | |
| groups.append("circle") | |
| .attr("class", "circle2") | |
| .attr("r", rad) | |
| .attr("cx", (w/1.4)) | |
| .attr("cy", function(d) {return yScale(d[1])}) | |
| .attr("stroke", function(d) { | |
| if (d[2] <= -10) {return "#000c60";} | |
| else if (d[2] >=0) {return "#569ffe";} | |
| else {return "#001bea";} | |
| }) | |
| .style("opacity", 1) | |
| ; | |
| // First text | |
| svg.append("text") | |
| .attr("x", (w/4)) | |
| .attr("y", h-(padding/2)) | |
| .style("text-anchor","middle") | |
| .text("First"); | |
| // Second text | |
| svg.append("text") | |
| .attr("x", (w/1.4)) | |
| .attr("y", h-(padding/2)) | |
| .style("text-anchor","middle") | |
| .text("Second"); | |
| //Create y axis | |
| svg.append("g") | |
| .attr("class", "axis") | |
| .attr("transform", "translate(" + (padding*1.5) + ",0)") | |
| .call(yAxis); | |
| // Add the text label for the Y axis | |
| svg.append("text") | |
| .attr("class", "ytext") | |
| .attr("transform", "rotate(-90)") | |
| .attr("y", (padding/100)) | |
| .attr("x",0 - (h / 2)) | |
| .attr("dy", "1em") | |
| .style("text-anchor", "middle") | |
| .text("Arbitrary Units"); | |
| function updateData() { | |
| //update data | |
| var mydataset = []; | |
| for(var i = 0; i < dataset.length; i++){ | |
| mydataset.push( | |
| [dataset[i].firstpct*100, dataset[i].secondpct*100, dataset[i].delta] | |
| ) | |
| } | |
| var xMax = d3.max(mydataset, function(d) { return d[0]; }) | |
| var yMax = d3.max(mydataset, function(d) { return d[1]; }) | |
| var max = Math.max(yMax, xMax); // highest value in dataset | |
| var yScale = d3.scale.linear() | |
| .domain([0, max]) | |
| .range([h - padding, padding]) | |
| .nice() | |
| ; | |
| //Update Y axis | |
| var yAxis = d3.svg.axis() | |
| .scale(yScale) | |
| .orient("left") | |
| .ticks(5); | |
| svg.select("g.axis") | |
| .transition() | |
| .duration(2000) | |
| .attr("class", "axis") | |
| .attr("transform", "translate(" + (padding*1.5) + ",0)") | |
| .call(yAxis); | |
| svg.selectAll("text.ytext").remove(); | |
| svg.append("text") | |
| .transition() | |
| .duration(2000) | |
| .attr("transform", "rotate(-90)") | |
| .attr("y", (padding/100)) | |
| .attr("x",0 - (h / 2)) | |
| .attr("dy", "1em") | |
| .style("text-anchor", "middle") | |
| .text("Percentage"); | |
| //Set-up Transition | |
| var groups = svg.selectAll("g") | |
| .data(mydataset) // bind data to the group | |
| .transition() | |
| .duration(2000); | |
| // update join paths | |
| groups.select("line") | |
| .attr("y1", function(d) {return yScale(d[0])}) | |
| .attr("y2", function(d) {return yScale(d[1])}) | |
| ; | |
| //update first background circles | |
| groups.select(".circles1b") | |
| .attr("cy", function(d) {return yScale(d[0])}) | |
| ; | |
| //update first circles | |
| groups.select(".circle1") | |
| .attr("cy", function(d) {return yScale(d[0])}) | |
| ; | |
| //update second background circles | |
| groups.select(".circles2b") | |
| .attr("cy", function(d) {return yScale(d[1])}) | |
| ; | |
| //update second circles | |
| groups.select(".circle2") | |
| .attr("cy", function(d) {return yScale(d[1])}) | |
| ; | |
| } | |
| </script> | |
| </body> | |