Skip to content

Instantly share code, notes, and snippets.

@powersa
Last active August 29, 2015 14:07
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save powersa/660a6c39e9e759535aa3 to your computer and use it in GitHub Desktop.
Save powersa/660a6c39e9e759535aa3 to your computer and use it in GitHub Desktop.
Partial Line Transitions in D3

This example illustrates progressive line animations in D3. This example applies to situations in which you want to add data to the front of a line or remove data from the end of a line. In this case, the transition is isolated to the data that changes and does not apply to the entire line.

<!DOCTYPE html>
<meta charset="utf-8">
<style>
svg {
font: 10px sans-serif;
}
.line {
fill: none;
stroke: #de2d26;
stroke-width: 2px;
}
.axis path,
.axis line {
fill: none;
stroke: #969696;
shape-rendering: crispEdges;
}
text {
color: #636363;
}
#variables {
font-size: 200%;
}
</style>
<body>
<script src="http://d3js.org/d3.v3.min.js"></script>
<script>
var margin = {top: 20, right: 20, bottom: 20, left: 40},
width = 1000 - margin.left - margin.right,
height = 500 - margin.top - margin.bottom;
var x = d3.scale.linear()
.domain([-1, 1])
.range([0, width/2]);
var y = d3.scale.linear()
.domain([-1, 1])
.range([height, 0]);
var xAxis = d3.svg.axis()
.scale(x)
.orient("bottom")
.tickValues([-1, -.5, .5, 1])
.tickSize(3, 6);
var yAxis = d3.svg.axis()
.scale(y)
.orient("left")
.tickValues([-1, -.5, .5, 1])
.tickSize(3, 6);
var line = d3.svg.line()
.x(function(d, i) { return x(d[0]); })
.y(function(d, i) { return y(d[1]); });
var svg = d3.select("body").append("svg")
.attr("width", width + margin.left + margin.right)
.attr("height", height + margin.top + margin.bottom)
.append("g")
.attr("transform", "translate(" + margin.left + "," + margin.top + ")");
svg.append("defs").append("clipPath")
.attr("id", "clip")
.append("rect")
.attr("width", width)
.attr("height", height);
svg.append("g")
.attr("class", "x axis")
.attr("transform", "translate(0," + y(0) + ")")
.call(xAxis);
svg.append("g")
.attr("class", "y axis")
.attr("transform", "translate(" + x(0) + ",0)")
.call(yAxis);
svg.append("g")
.attr("id", "variables")
.attr("transform", "translate(650,0)");
svg.select("#variables")
.append("text")
.attr("id", "type")
.attr("transform", "translate(0,10)");
svg.select("#variables")
.append("text")
.attr("id", "t")
.text("t: ")
.attr("transform", "translate(0,50)");
svg.select("#variables")
.append("text")
.attr("id", "point_a")
.text("point_a:")
.attr("transform", "translate(0,90)");
svg.select("#variables")
.append("text")
.attr("id", "point_i")
.text("point_i:")
.attr("transform", "translate(0,130)");
svg.select("#variables")
.append("text")
.attr("id", "point_b")
.text("point_b:")
.attr("transform", "translate(0,170)");
window.onload=enqueue('dev');
function random() {
var num = Math.random();
return ((num - .5)*2).toFixed(2);
};
function random_array() {
return [random(), random()]
};
function create_queue(id, data) {
svg.append("g")
.append("path")
.data([data])
.attr("class", "line")
.attr("id", id)
.attr("d", line);
enqueue(id);
}
function enqueue(id) {
// display
svg.select("#type").text("function: enqueue");
// assign/select unique id to enable multiple paths
var target = "#" + id,
path = svg.select(target);
if ( path.empty() ) {
// create new queue
create_queue(id, d3.range(5).map(random_array));
} else {
// push point into rear of queue
path.data()[0].push([random(), random()]);
// redraw the line
path
.transition()
.duration(4000)
.ease("linear")
.attrTween('d', enqueueInter());
}
}
function enqueueInter() {
return function (d, i, a) {
var point_a = d[d.length-2],
point_b = d[d.length-1];
// display
svg.select("#point_a").text("point_a: " + point_a);
svg.select("#point_b").text("point_b: " + point_b);
var interpolateX = d3.scale.linear()
// time value is between 0 and 1
.domain([0,1])
// coerce numbers between starting and ending x value
.range([point_a[0], point_b[0]]);
return function(t) {
while(t != 1) {
// display
svg.select("#t").text("t: " + t.toFixed(2))
// cut data to static coords
var interpolatedLine = d.slice(0, d.length-1),
interpolatedY = point_b[1] * t + point_a[1] * (1-t),
point_i = [interpolateX(t), interpolatedY];
// add interpolated coord to rear
interpolatedLine.push(point_i);
//display
svg.select("#point_i").text("point_i: " + parseFloat(point_i[0]).toFixed(2) + "," + parseFloat(point_i[1]).toFixed(2))
// draw interpolated line
return line(interpolatedLine);
}
// draw final line
dequeue('dev');
return line(d)
}
}
}
function dequeue(id) {
// display
svg.select("#type").text("function: dequeue");
// assign/select unique id to enable multiple paths
var query = "#" + id,
path = svg.select(query);
if ( path.data()[0].length == 1 ) {
// remove queue
path.remove();
} else {
path
.transition()
.duration(4000)
.ease("linear")
.attrTween('d', dequeueInter());
}
}
function dequeueInter() {
return function (d, i, a) {
var point_a = d[0],
point_b = d[1];
// display
svg.select("#point_a").text("point_a: " + point_a);
svg.select("#point_b").text("point_b: " + point_b);
var interpolateX = d3.scale.linear()
// time value is between 0 and 1
.domain([0,1])
// coerce numbers between starting and ending x value
.range([point_a[0], point_b[0]]);
return function(t) {
while(t != 1) {
// display
svg.select("#t").text("t: " + t.toFixed(2))
// cut data to static coords
var interpolatedLine = d.slice(1, d.length),
interpolatedY = point_b[1] * t + point_a[1] * (1-t),
point_i = [interpolateX(t), interpolatedY];
//display
svg.select("#point_i").text("point_i: " + parseFloat(point_i[0]).toFixed(2) + "," + parseFloat(point_i[1]).toFixed(2))
// add interpolated coord to front
interpolatedLine.unshift(point_i);
// draw interpolated line
return line(interpolatedLine);
}
// remove coord from front
d.shift()
enqueue('dev');
// draw final line
return line(d);
}
}
}
</script>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment