Created
June 14, 2016 08:32
-
-
Save puzzler10/fe9d75969a0d2f40f003f00808c00f3b to your computer and use it in GitHub Desktop.
Grouped, Stacked, Scatter and Line
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<!DOCTYPE html> | |
<html> | |
<head> | |
<style> | |
body { | |
font-family: "Helvetica Neue", Helvetica, Arial, sans-serif; | |
margin: auto; | |
position: relative; | |
width: 960px; | |
} | |
text { | |
font: 10px sans-serif; | |
} | |
.axis path, | |
.axis line { | |
fill: none; | |
stroke: #000; | |
shape-rendering: crispEdges; | |
} | |
form { | |
position: absolute; | |
right: 10px; | |
top: 10px; | |
} | |
.line { | |
fill: none; | |
stroke-width: 3px; | |
} | |
</style> | |
</head> | |
<body> | |
<form> | |
<label><input type="radio" name="mode" value="grouped"> Grouped </label> | |
<label><input type="radio" name="mode" value="stacked" checked> Stacked</label> | |
<label><input type="radio" name="mode" value="scatter"> Scatter</label> | |
<label><input type="radio" name="mode" value="line"> Line </label> | |
</form> | |
<script src="https://d3js.org/d3.v3.min.js" charset="utf-8"></script><script> | |
//https://bl.ocks.org/mbostock/3943967 | |
var n = 4 // number of layers | |
var m = 60 // number of data points | |
var stack = d3.layout.stack() | |
var layers = stack(d3.range(n).map(function() {return bumpLayer(m, 0.1)})) | |
var yGroupMax = d3.max(layers, function(column) {return d3.max(column, function(row){return row.y})}) | |
var yScatterMax = d3.max(layers, function(column) {return d3.max(column, function(row) {return row.y})}) | |
var yStackMax = d3.max(layers, function(column) {return d3.max(column, function(row){return row.y + row.y0})}) | |
var margin = {top:40, bottom:20, right:10, left:10} | |
var svgWidth = 960 - margin.left - margin.right | |
var svgHeight = 500 - margin.top - margin.bottom | |
var svg = d3.select("body").append("svg") | |
.attr("width", svgWidth + margin.left + margin.right) | |
.attr("height", svgHeight + margin.top + margin.bottom) | |
.append("g") | |
.attr("transform", "translate(" + margin.left + "," + margin.top + ")"); | |
var xScale = d3.scale.ordinal() | |
.domain(d3.range(m)) | |
.rangeRoundBands([0,svgWidth], 0.05); | |
var yScale = d3.scale.linear() | |
.domain([0, yStackMax]) | |
.range([svgHeight, 0]); | |
var xAxis = d3.svg.axis() | |
.scale(xScale) | |
.tickSize(0) | |
.tickPadding(6) | |
.orient("bottom"); | |
svg.append("g") | |
.attr("class", "x axis") | |
.attr("transform", "translate(0," + (svgHeight) + ")") | |
.call(xAxis); | |
var line = d3.svg.line() | |
.x(function(d) { return xScale(d.x); }) | |
.y(function(d) { return yScale(d.y); }) | |
// .interpolate("basis"); | |
var lineBefore = false | |
var layer = svg.selectAll(".layer") | |
.data(layers) | |
.enter() | |
.append("g") | |
.attr("class", "layer") | |
.style("fill", function(d,i) {return chooseCol(i)}) | |
//Start with Stacked | |
var rect = layer.selectAll("rect") | |
.data(function(d) {return d}) | |
.enter() | |
.append("rect") | |
.attr("x", function(d) {return xScale(d.x)}) | |
.attr("y", svgHeight) | |
.attr("width", xScale.rangeBand()) | |
.attr("height", 0) | |
rect.transition() | |
.delay(function(d,i){ return i*10 } ) | |
.attr("y", function(d) {return yScale(d.y0 + d.y)}) | |
.attr("height", function(d){return yScale(d.y0) - yScale(d.y0 + d.y)} ) | |
//for line graph | |
var path = layer.append("path") | |
.style("fill", "none") | |
.attr("class", "line") | |
d3.selectAll("input").on("change", change); | |
function change() | |
{ | |
if (this.value === "grouped") | |
{ transitionGrouped(); } | |
else if (this.value === "stacked") | |
{ transitionStacked(); } | |
else if(this.value === "scatter") | |
{ transitionScatter();} | |
else if(this.value === "line") | |
{ transitionLine();} | |
} | |
function transitionLine() | |
{ | |
lineBefore = true //indicates if we've selected this before | |
yScale.domain([0, yGroupMax]); | |
path.attr("d", function(d) {return line(d)}) | |
.style("stroke", function(d,i) {return chooseCol(i)} ) | |
//This works using https://css-tricks.com/svg-line-animation-works/ | |
.each(function(d) { d.totalLength = this.getTotalLength(); }) | |
.attr("stroke-dasharray",function(d){ return(d.totalLength + " " + d.totalLength)}) | |
.attr("stroke-dashoffset", function(d) {return d.totalLength}) | |
//fade out rectangles/points | |
rect.transition() | |
.delay(1000) | |
.duration(250) | |
.attr("height",0) | |
.attr("width", 0); | |
//draw line | |
path.transition() | |
.duration(1000) | |
.ease("linear") | |
.attr("stroke-dashoffset", 0); | |
} | |
//Grouped graph | |
function transitionGrouped() | |
{ | |
yScale.domain([0, yGroupMax]); | |
rect.transition() | |
.duration(500) | |
.delay(function(d, i) { return i * 10; }) | |
.attr("x", function(d, i, j) { return xScale(d.x) + xScale.rangeBand() / n * j; }) | |
.attr("width", xScale.rangeBand() / n) | |
.attr("rx", 0) | |
.attr("ry", 0) | |
.transition() | |
.attr("y", function(d) { return yScale(d.y); }) | |
.attr("height", function(d) { return svgHeight - yScale(d.y); }); | |
unwindLine(0) | |
} | |
function transitionStacked() | |
{ | |
yScale.domain([0,yStackMax]) | |
rect.transition() | |
.duration(500) | |
.delay(function(d,i) {return i*10;}) | |
.attr("rx", 0) | |
.attr("ry", 0) | |
.attr("y", function(d) {return yScale(d.y0 + d.y)}) | |
.attr("height", function(d){return yScale(d.y0) - yScale(d.y0 + d.y)}) | |
.transition() | |
.attr("x", function(d) {return xScale(d.x)}) | |
.attr("width", xScale.rangeBand()) | |
unwindLine(0) | |
} | |
function transitionScatter() | |
{ | |
yScale.domain([0, yGroupMax]); | |
//have to adjust absolute position to get centring effect | |
topLeft = xScale.rangeBand() / n | |
rect.transition() | |
.duration(500) | |
.delay(function(d,i) {return i*10;}) | |
.attr("width",topLeft) | |
.attr("x", function(d) { return xScale(d.x) -topLeft/2 }) | |
.transition() | |
.attr("height", topLeft ) | |
.attr("y", function(d) { return yScale(d.y) - topLeft/2 }) | |
.attr("rx", 30) | |
.attr("ry", 30) | |
unwindLine(1000) | |
} | |
function unwindLine(delay) | |
//function to undraw the line when we deselect it | |
{ | |
if (lineBefore === true) | |
{ | |
lineBefore === false | |
path.transition() | |
.delay(delay) | |
.duration(300) | |
.ease("linear") | |
.attr("stroke-dashoffset", function(d) {return d.totalLength}); | |
} | |
} | |
function chooseCol(rowNo) | |
{ | |
if(rowNo=== 0){ | |
return("#e41a1c") | |
} else if (rowNo === 1) { | |
return("#377eb8") | |
} else if(rowNo === 2) { | |
return("#4daf4a") | |
} else{ | |
return ("#984ea3") | |
} | |
} | |
// Inspired by Lee Byron's test data generator. | |
function bumpLayer(n, o) { | |
function bump(a) { | |
// x will take values between 0.90909 and 10 | |
var x = 1 / (.1 + Math.random()), | |
//y takes values between -0.5 and 1.5 | |
y = 2 * Math.random() - .5, | |
//z takes values between 9.0909 and 100 | |
z = 10 / (.1 + Math.random()); | |
for (var i = 0; i < n; i++) { | |
//(i/10 - y) can take values between -1.5 and 1.5 | |
//so w takes range between -150 and 150 | |
//each time the loop goes round, w is incremented by the same amount. | |
//the increment goes up by between 0.90909 and 10 | |
var w = (i / n - y) * z; | |
//x += 2 is the same as x = x + 2 | |
//a[i] = a[i] + ... | |
a[i] += x * Math.exp(-w * w); | |
} | |
} | |
var a = [], i; | |
for (i = 0; i < n; ++i) a[i] = o + o * Math.random(); | |
for (i = 0; i < 5; ++i) bump(a); | |
return a.map(function(d, i) { return {x: i, y: Math.max(0, d)}; }); | |
} | |
</script> | |
</body> | |
</html> |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment