Added axis considerations, data loading via picklist and styles.
Pulled a lot of great animation functionality from this blog post
http://big-elephants.com/2014-06/unrolling-line-charts-d3js/
Added axis considerations, data loading via picklist and styles.
Pulled a lot of great animation functionality from this blog post
http://big-elephants.com/2014-06/unrolling-line-charts-d3js/
var data = { | |
quarterly:[{ | |
date:"Q3 14'", | |
numberOfThings:2222, | |
percentageOfThings:20, | |
priceOfThings:50 | |
},{ | |
date:"Q4 14'", | |
numberOfThings:2780, | |
percentageOfThings:29, | |
priceOfThings:68 | |
},{ | |
date:"Q1 15'", | |
numberOfThings:3301, | |
percentageOfThings:31, | |
priceOfThings:69 | |
},{ | |
date:"Q2 15'", | |
numberOfThings:2700, | |
percentageOfThings:29, | |
priceOfThings:66 | |
},{ | |
date:"Q3 15'", | |
numberOfThings:2000, | |
percentageOfThings:19, | |
priceOfThings:54 | |
},{ | |
date:"Q4 15'", | |
numberOfThings:2502, | |
percentageOfThings:25, | |
priceOfThings:62 | |
},{ | |
date:"Q1 16'", | |
numberOfThings:3290, | |
percentageOfThings:29, | |
priceOfThings:70 | |
}], | |
monthly:[{ | |
date:"Sept 15'", | |
numberOfThings:1290, | |
percentageOfThings:20, | |
priceOfThings:18 | |
},{ | |
date:"Oct 15'", | |
numberOfThings:934, | |
percentageOfThings:25, | |
priceOfThings:19 },{ | |
date:"Nov 15'", | |
numberOfThings:1054, | |
percentageOfThings:23, | |
priceOfThings:20 | |
},{ | |
date:"Dec 15'", | |
numberOfThings:500, | |
percentageOfThings:18, | |
priceOfThings:10 },{ | |
date:"Jan 16'", | |
numberOfThings:1552, | |
percentageOfThings:33, | |
priceOfThings:24 | |
},{ | |
date:"Feb 16'", | |
numberOfThings:1212, | |
percentageOfThings:30, | |
priceOfThings:12 | |
},{ | |
date:"Mar 16'", | |
numberOfThings:1021, | |
percentageOfThings:27, | |
priceOfThings:14 | |
}], | |
weekly:[{ | |
date:"03/05/2016", | |
numberOfThings:5, | |
percentageOfThings:4, | |
priceOfThings:0.1 | |
},{ | |
date:"03/06/2016", | |
numberOfThings:2, | |
percentageOfThings:1, | |
priceOfThings:0.05},{ | |
date:"03/07/2016", | |
numberOfThings:52, | |
percentageOfThings:20, | |
priceOfThings:0.69 | |
},{ | |
date:"03/08/2016", | |
numberOfThings:40, | |
percentageOfThings:25, | |
priceOfThings:0.66 | |
},{ | |
date:"03/09/2016", | |
numberOfThings:90, | |
percentageOfThings:33, | |
priceOfThings:0.70 | |
},{ | |
date:"03/10/2016", | |
numberOfThings:70, | |
percentageOfThings:29, | |
priceOfThings:0.75 | |
},{ | |
date:"03/11/2016", | |
numberOfThings:20, | |
percentageOfThings:15, | |
priceOfThings:0.5 | |
}] | |
}; |
<!DOCTYPE html> | |
<html> | |
<head> | |
<title>D3 Timeseries</title> | |
<script src="//d3js.org/d3.v3.min.js" charset="utf-8"></script> | |
<script src="https://ajax.googleapis.com/ajax/libs/jquery/1.12.0/jquery.min.js"></script> | |
<script type="text/javascript" src="data.js"></script> | |
<link rel="stylesheet" type="text/css" href="lineGraph.css"/> | |
</head> | |
<body> | |
<div id="graph-title">Basic Updatable D3 Timeseries</div> | |
<div id="graph-picklist-wrapper"> | |
<text>View</text> | |
<select id="graph-picklist-y" class="graph-picklist" onchange="refreshGraph()"> | |
<option value="numberOfThings" selected>Number of Things</option> | |
<option value="percentageOfThings">% Utility</option> | |
<option value="priceOfThings">Cost Of Things</option> | |
</select> | |
<text>By</text> | |
<select id="graph-picklist-x" class="graph-picklist" onchange="refreshGraph()"> | |
<option value="weekly" selected>Week</option> | |
<option value="monthly">Month</option> | |
<option value="quarterly">Quarter</option> | |
</select> | |
</div> | |
<div id="graph-container"></div> | |
</body> | |
<script type="text/javascript" src="lineGraph.js"></script> | |
</html> |
body{ | |
font-family: Helvetica; | |
} | |
#graph-container{ | |
position: relative; | |
display: block; | |
height: 300px; | |
width:90%; | |
margin-left: 5%; | |
} | |
#graph-picklist-wrapper{ | |
text-align: center; | |
} | |
#graph-title{ | |
display: block; | |
position: relative; | |
width: 70%; | |
margin-left: 15%; | |
text-align: center; | |
font-size: 20px; | |
border-bottom: thin solid black; | |
margin-bottom: 30px; | |
margin-top: 20px; | |
padding-bottom: 10px; | |
} | |
path.line { | |
fill: none; | |
stroke:steelblue; | |
stroke-width: 3px; | |
} | |
path.area { | |
fill:rgba(70,130,180,0.5); | |
opacity: 0; | |
} | |
/*.axis { | |
shape-rendering: crispEdges; | |
} | |
*/ | |
.x.axis .minor { | |
stroke: black; | |
} | |
.x.axis path { | |
display: none; | |
stroke: none; | |
} | |
.y.axis line, .y.axis path { | |
fill: none; | |
stroke: none; | |
} | |
.y.axis line{ | |
stroke:#d3d3d3; | |
stroke-width:1px; | |
} | |
.x.axis line{ | |
stroke:#d3d3d3; | |
stroke-width:1px; | |
} | |
.tick text{ | |
stroke:black; | |
font-size: 12px; | |
font-family: openSansLight; | |
} | |
.x.axis text{ | |
margin-top: 10px; | |
} | |
#y-axis-label{ | |
stroke:black; | |
font-size: 14px; | |
font-weight:200; | |
} |
function fetchFacetData(d,timeScale,facet){ | |
returnArr =[]; | |
d.forEach(function(d){ | |
(timeScale==="weekly")? | |
returnArr.push({date: new Date(d.date), value: d[facet]}) : | |
returnArr.push({date: d.date, value: d[facet]}); | |
}); | |
return returnArr; | |
} | |
function prettyPrintYAxis(facet){ | |
switch(facet){ | |
case "numberOfThings": | |
return "Number of Things" | |
break; | |
case "percentageOfThings": | |
return "% Utility" | |
break; | |
case "priceOfThings": | |
return "Cost ($M)" | |
break; | |
default: | |
return "Y Axis Label"; | |
} | |
} | |
function renderLineGraph(facet,timeScale,passedData){ | |
var animDuration = 1000; | |
//console.log(facet,timeScale,passedData); | |
var margin = {top: 8, right: 70, bottom: 35, left: 45}, | |
width = $("#graph-container").width() - margin.left - margin.right, | |
height = $("#graph-container").height() - margin.top - margin.bottom; | |
var x = d3.time.scale().range([0, width]); | |
var y = d3.scale.linear().range([height, 0]); | |
var yAxis = d3.svg.axis().scale(y).ticks(4).tickSize(-width).orient("right"); | |
x.domain([new Date(passedData[0].date), new Date(passedData[passedData.length - 1].date)]); | |
y.domain([0, d3.max(passedData, function(d) { return d[facet]; })]).nice(); | |
switch(timeScale){ | |
case "weekly": | |
var xAxis = d3.svg.axis().scale(x).ticks(passedData.length).tickSize(-height).tickFormat(d3.time.format("%a %m/%d")).tickPadding(15); | |
break; | |
case "monthly": | |
x = d3.scale.ordinal().domain(["Sept 15'","Oct 15'","Nov 15'","Dec 15'","Jan 16'","Feb 16'","Mar 16'"]).rangePoints([0, width]); | |
var xAxis = d3.svg.axis().scale(x).ticks(passedData.length).tickSize(-height).tickPadding(15); | |
break; | |
case "quarterly": | |
x = d3.scale.ordinal().domain(["Q3 14'","Q4 14'","Q1 15'","Q2 15'","Q3 15'","Q4 15'","Q1 16'"]).rangePoints([0, width]); | |
var xAxis = d3.svg.axis().scale(x).ticks(passedData.length).tickSize(-height).tickPadding(15); | |
break; | |
default: | |
console.log("switch error on yAxis assignment"); | |
var xAxis = d3.svg.axis().scale(x).ticks(passedData.length).tickSize(-height).tickFormat(d3.time.format("%a %m/%d")).tickPadding(15); | |
break; | |
} | |
var area = d3.svg.area() | |
.x(function(d) {return x(d.date); }) | |
.y0(height) | |
.y1(function(d) { return y(d.value); }) | |
.interpolate("monotone"); | |
// A line generator, for the dark stroke. | |
var line = d3.svg.line() | |
.x(function(d) {return x(d.date);}) | |
.y(function(d) {return y(d.value);}) | |
.interpolate("monotone"); | |
// Add an SVG element with the desired dimensions and margin. | |
var wrapper = d3.select("#graph-container"); | |
var svg = wrapper.append("svg") | |
.attr("width", width + margin.left + margin.right) | |
.attr("height", height + margin.top + margin.bottom) | |
.attr("id","chart-svg-container") | |
.append("g") | |
.attr("transform", "translate(" + margin.left + "," + margin.top + ")") | |
// Add the x-axis. | |
svg.append("g") | |
.attr("class", "x axis") | |
.attr("transform", "translate(0," + (height) + ")") | |
.call(xAxis); | |
// Add the y-axis. | |
svg.append("g") | |
.attr("class", "y axis") | |
.attr("transform", "translate(" + (width)+ ",0)") | |
.call(yAxis); | |
svg.append('g') | |
.attr('transform', 'translate(' + (width+margin.right/1.5) + ', ' + (height/2) + ')') | |
.append('text') | |
.attr('text-anchor', 'middle') | |
.attr("id","y-axis-label") | |
.attr('transform', 'rotate(90)') | |
.text(prettyPrintYAxis(facet)); | |
var facetData = fetchFacetData(passedData,timeScale,facet); | |
svg.selectAll('.line') | |
.data([facetData]) | |
.enter() | |
.append('path') | |
.attr('class', 'line') | |
.attr("id","current-path") | |
.style('stroke',"steelblue") | |
.attr('d',function(d){return line(d);}); | |
svg.append("path") | |
.data([facetData]) | |
.attr("class", "area") | |
.attr("d", function(d){return area(d);}); | |
var totalLength = d3.select("#current-path").node().getTotalLength(); | |
d3.select("#current-path") | |
.attr("stroke-dasharray", totalLength + " " + totalLength) | |
.attr("stroke-dashoffset", totalLength) | |
.transition() | |
.duration(animDuration/1.5) | |
.ease("linear") | |
.attr("stroke-dashoffset", 0); | |
d3.selectAll(".area").transition().delay(animDuration).duration(animDuration).style("opacity",1); | |
} | |
function refreshGraph(){ | |
$("#chart-svg-container").remove(); | |
renderLineGraph($("#graph-picklist-y").val(),$("#graph-picklist-x").val(),data[$("#graph-picklist-x").val()]); | |
} | |
$(document).ready(function() { | |
renderLineGraph('numberOfThings','weekly',data.weekly); | |
}); | |