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