-
-
Save slindberg/4535474 to your computer and use it in GitHub Desktop.
Custom path interpolator
This file contains hidden or 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
<html> | |
<head> | |
<title>Chart</title> | |
<style> | |
path { | |
stroke: #f00; | |
} | |
.line { | |
stroke: #0f0; | |
fill: none; | |
stroke-width: 2px; | |
} | |
.rule { | |
stroke: #ccc; | |
stroke-width: 1px; | |
} | |
</style> | |
</head> | |
<body> | |
<p>I want to get the chart below to transition such that | |
the points on the lines appear to move up and down, not | |
side to side. | |
</p> | |
<p>When transitioning to the smaller data-set especially, | |
I'd like to not have a white gap appear before the lines | |
take shape. | |
</p> | |
<p>Also, the grid-lines should slide into and out of | |
existence, rather than appearing or disappearing. Ideas? | |
</p> | |
<script src="http://d3js.org/d3.v2.min.js"></script> | |
<script> | |
var data = [ | |
[0,2,3,2,8], | |
[2,4,1,5,3], | |
]; | |
var data2 = [ | |
[0,1,2,3,4,5], | |
[9,8,7,6,5,6], | |
]; | |
var data3 = [ | |
[1,3,2], | |
[0,8,5], | |
]; | |
var w = 300, | |
h = 100; | |
var chart = d3.select('body').append('div') | |
.attr('class', 'chart') | |
.append('svg:svg') | |
.attr('width', w) | |
.attr('height', h); | |
var color = d3.scale.category10(); | |
// Add path interpolator to d3 | |
d3.interpolators.push(function(a, b) { | |
var isPath, isArea, interpolator, ac, bc, an, bn, d; | |
// Create a new array of a given length and fill it with the given value | |
function fill(value, length) { | |
return d3.range(length) | |
.map(function() { | |
return value; | |
}); | |
} | |
// Extract an array of coordinates from the path string | |
function extractCoordinates(path) { | |
return path.substr(1, path.length - (isArea ? 2 : 1)).split('L'); | |
} | |
// Create a path from an array of coordinates | |
function makePath(coordinates) { | |
return 'M' + coordinates.join('L') + (isArea ? 'Z' : ''); | |
} | |
// Buffer the smaller path with coordinates at the same position | |
function bufferPath(p1, p2) { | |
var d = p2.length - p1.length; | |
if (isArea) { | |
return fill(p1[0], d/2).concat(p1, fill(p1[p1.length - 1], d/2)); | |
} else { | |
return fill(p1[0], d).concat(p1); | |
} | |
} | |
isPath = /M-?\d*\.?\d*,-?\d*\.?\d*(L-?\d*\.?\d*,-?\d*\.?\d*)*Z?/; | |
if (isPath.test(a) && isPath.test(b)) { | |
isArea = a[a.length - 1] === 'Z'; | |
ac = extractCoordinates(a); | |
bc = extractCoordinates(b); | |
an = ac.length; | |
bn = bc.length; | |
if (an > bn) { | |
bc = bufferPath(bc, ac); | |
} | |
if (bn > an) { | |
ac = bufferPath(ac, bc); | |
} | |
// Create an interpolater with the buffered paths (if both paths are of the same length, | |
// the function will end up being the default string interpolator) | |
interpolator = d3.interpolateString(bn > an ? makePath(ac) : a, an > bn ? makePath(bc) : b); | |
// If the ending value changed, make sure the final interpolated value is correct | |
return bn > an ? interpolator : function(t) { | |
return t === 1 ? b : interpolator(t); | |
}; | |
} | |
}); | |
function drawdata(data, chart) { | |
var num = data[0].length-1; | |
var x = d3.scale.linear().domain([0, num]).range([0,w]); | |
var y = d3.scale.linear().domain([0, 10]).range([h, 0]); | |
var line = d3.svg.line() | |
.x(function(d, i) { return x(i); }) | |
.y(function(d) { return y(d); }); | |
var flat = d3.svg.line() | |
.x(function(d, i) { return x(i); }) | |
.y(y(-1)); | |
var lines = chart.selectAll('.line') | |
.data(data); | |
lines.enter().append('path') | |
.attr('class', 'line') | |
.style('stroke', function(d,i) { return color(i); }) | |
.attr('d', line); | |
lines.transition() | |
.ease('linear') | |
.duration(500) | |
.attr('d', line); | |
lines.exit().remove(); | |
// legend | |
var ticks = chart.selectAll('line') | |
.data(x.ticks(num)); | |
ticks.enter().append('line') | |
.attr('x1', x) | |
.attr('x2', x) | |
.attr('y1', 0) | |
.attr('y2', h) | |
.attr('class', 'rule'); | |
ticks.transition() | |
.ease('linear') | |
.duration(500) | |
.attr('x1', x) | |
.attr('x2', x) | |
.attr('y1', 0) | |
.attr('y2', h); | |
ticks.exit().remove(); | |
} | |
var dats = [data, data2, data3]; | |
function next() { | |
var it = dats.shift(); | |
dats.push(it); | |
drawdata(it, chart); | |
} | |
setInterval(next, 2000); | |
next(); | |
</script> | |
</body> | |
</html> |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment