Create a gist now

Instantly share code, notes, and snippets.

@mbostock /.block
Last active Jun 21, 2018

Embed
Line Transition
license: gpl-3.0

This example is the second of three in the Path Transitions tutorial; see the previous example for context.

The desired pairing of numbers for path interpolation is like this:

M x0, y0 L x1, y1 L x2, y2 L x3, y3 L xR, y4
   ↓   ↓    ↓   ↓    ↓   ↓    ↓   ↓
M xl, y0 L x0, y1 L x1, y2 L x2, y3 L x3, y4

Where xl is some negative value off the left side, and xr is some positive value off the right side. This way, the first point ⟨x0,y0⟩ is interpolated to ⟨xl,y0⟩; meaning, the x-coordinate is interpolated rather than the y-coordinate, and so the path appears to slide off to the left. Likewise, the incoming point ⟨xr,y4⟩ is interpolated to ⟨x3,y4⟩.

While you could write a custom interpolator and use transition.attrTween to achieve this, a much simpler solution is to interpolate the transform attribute rather than the path. This way, the shape of the path remains static while the it translates left during the transition.

Immediately prior to the transition, the path is redrawn as follows:

M x0, y0 L x1, y1 L x2, y2 L x3, y3 L xr, y4

Then, a transform transition is applied:

translate(0,0)
          ↓
translate(xl,0)

This causes the path to slide left. A clip path is used so the path is not visible outside of the chart body.

Note that for charts with spline interpolation, you’ll need to crop the visible part of the line by an extra point, so that the change in tangent is not visible; see the next example.

<!DOCTYPE html>
<meta charset="utf-8">
<style>
.line {
fill: none;
stroke: #000;
stroke-width: 1.5px;
}
</style>
<svg width="960" height="500"></svg>
<script src="//d3js.org/d3.v4.min.js"></script>
<script>
var n = 40,
random = d3.randomNormal(0, .2),
data = d3.range(n).map(random);
var svg = d3.select("svg"),
margin = {top: 20, right: 20, bottom: 20, left: 40},
width = +svg.attr("width") - margin.left - margin.right,
height = +svg.attr("height") - margin.top - margin.bottom,
g = svg.append("g").attr("transform", "translate(" + margin.left + "," + margin.top + ")");
var x = d3.scaleLinear()
.domain([0, n - 1])
.range([0, width]);
var y = d3.scaleLinear()
.domain([-1, 1])
.range([height, 0]);
var line = d3.line()
.x(function(d, i) { return x(i); })
.y(function(d, i) { return y(d); });
g.append("defs").append("clipPath")
.attr("id", "clip")
.append("rect")
.attr("width", width)
.attr("height", height);
g.append("g")
.attr("class", "axis axis--x")
.attr("transform", "translate(0," + y(0) + ")")
.call(d3.axisBottom(x));
g.append("g")
.attr("class", "axis axis--y")
.call(d3.axisLeft(y));
g.append("g")
.attr("clip-path", "url(#clip)")
.append("path")
.datum(data)
.attr("class", "line")
.transition()
.duration(500)
.ease(d3.easeLinear)
.on("start", tick);
function tick() {
// Push a new data point onto the back.
data.push(random());
// Redraw the line.
d3.select(this)
.attr("d", line)
.attr("transform", null);
// Slide it to the left.
d3.active(this)
.attr("transform", "translate(" + x(-1) + ",0)")
.transition()
.on("start", tick);
// Pop the old data point off the front.
data.shift();
}
</script>
@canimus

This comment has been minimized.

Show comment
Hide comment
@canimus

canimus Jan 20, 2012

Very nice example!
Quick question: How does your CPU look when running in Google Chrome?
Do you experience a resource intense behavior?
Thanks!

canimus commented Jan 20, 2012

Very nice example!
Quick question: How does your CPU look when running in Google Chrome?
Do you experience a resource intense behavior?
Thanks!

@edhemphill

This comment has been minimized.

Show comment
Hide comment
@edhemphill

edhemphill Oct 9, 2012

thanks for explaining this further here.

thanks for explaining this further here.

@kfrajtak

This comment has been minimized.

Show comment
Hide comment

kfrajtak commented Apr 8, 2013

Can you please help me with my problem (http://stackoverflow.com/questions/15863877/d3-multi-line-chart-strange-animation/). Thank you, Karel

@sgizm

This comment has been minimized.

Show comment
Hide comment
@sgizm

sgizm May 10, 2013

Hello, I have a question: I want to use some external data with this graph. Can i learn which kind of data format i should have?

sgizm commented May 10, 2013

Hello, I have a question: I want to use some external data with this graph. Can i learn which kind of data format i should have?

@ShangBella

This comment has been minimized.

Show comment
Hide comment
@ShangBella

ShangBella May 22, 2013

Hi Mike, Thanks for this nice example. But when I try it in my browser,There is one error which keeps appearing"Unexpected value null parsing transform attribute.", as it running,this error repeats again and again. Do you notice this error and have any solution for it ?

Hi Mike, Thanks for this nice example. But when I try it in my browser,There is one error which keeps appearing"Unexpected value null parsing transform attribute.", as it running,this error repeats again and again. Do you notice this error and have any solution for it ?

@sampo321

This comment has been minimized.

Show comment
Hide comment
@sampo321

sampo321 Aug 19, 2013

< .attr("transform", null)

.attr("transform", "")
then works in ie

< .attr("transform", null)

.attr("transform", "")
then works in ie

@ghost

This comment has been minimized.

Show comment
Hide comment
@ghost

ghost Jun 5, 2014

To canimus

Probably too late but it only runs the cpu hard if you require that it preform the ticks faster. He only has them every 500 milliseconds. I'm using a similar model for tracking mouse movements and it takes up quite a bit of cpu.

ghost commented Jun 5, 2014

To canimus

Probably too late but it only runs the cpu hard if you require that it preform the ticks faster. He only has them every 500 milliseconds. I'm using a similar model for tracking mouse movements and it takes up quite a bit of cpu.

@fbukevin

This comment has been minimized.

Show comment
Hide comment
@fbukevin

fbukevin May 13, 2015

It is awesome!!!

It is awesome!!!

@quacainia

This comment has been minimized.

Show comment
Hide comment
@quacainia

quacainia Jun 4, 2015

.transition()
      .duration(500)
      .ease("linear")
      .attr("transform", "translate(" + x(-1) + ",0)")
      .each("end", tick);

.each(,) runs a new function "tick" for each point after each iteration, so it'll exponentially grow per cycle. I left this open on a second tab and it ran my CPU into the ground.

.transition()
      .duration(500)
      .ease("linear")
      .attr("transform", "translate(" + x(-1) + ",0)")
      .each("end", tick);

.each(,) runs a new function "tick" for each point after each iteration, so it'll exponentially grow per cycle. I left this open on a second tab and it ran my CPU into the ground.

@base698

This comment has been minimized.

Show comment
Hide comment
@base698

base698 Jul 14, 2015

Anyone know how to animate the x axis? I have a data stream that should increase and change in the x and y. Also the domain will change. Haven't found any good examples.

base698 commented Jul 14, 2015

Anyone know how to animate the x axis? I have a data stream that should increase and change in the x and y. Also the domain will change. Haven't found any good examples.

@sunking

This comment has been minimized.

Show comment
Hide comment
@sunking

sunking Oct 30, 2015

I also want to animate the x-axis like it was in the last example. Unfortunately, there is no sample code given, I am unable to reproduce a graph with a data stream along a moving x-axis.

sunking commented Oct 30, 2015

I also want to animate the x-axis like it was in the last example. Unfortunately, there is no sample code given, I am unable to reproduce a graph with a data stream along a moving x-axis.

@bjorngi

This comment has been minimized.

Show comment
Hide comment
@bjorngi

bjorngi Dec 9, 2015

@sunking @base698 check out the source code for the site.
Pulled out code for last example
https://gist.github.com/bjorngi/00be6a13e1bede83cbef

bjorngi commented Dec 9, 2015

@sunking @base698 check out the source code for the site.
Pulled out code for last example
https://gist.github.com/bjorngi/00be6a13e1bede83cbef

@oller

This comment has been minimized.

Show comment
Hide comment
@oller

oller Feb 4, 2016

Thanks for this @mbostock.

I'm trying to get my head around these magic numbers and how they interact with each other. This pertains to the gist @bjorngi posted above...

n = 243,
duration = 750

Is it just the case that :
n the length of the data array
duration the time in ms between iterating over these

So 243 * 750ms ~= 180s which is the 3 minute width of the x axis.

My scenario is I'll have an unknown length of data coming (via websockets) but will know the length of time I'd like the x axis to represent. Unsure If I'll have to buffer this data to update the chart on a more regular basis.

Any insight appreciated!

oller commented Feb 4, 2016

Thanks for this @mbostock.

I'm trying to get my head around these magic numbers and how they interact with each other. This pertains to the gist @bjorngi posted above...

n = 243,
duration = 750

Is it just the case that :
n the length of the data array
duration the time in ms between iterating over these

So 243 * 750ms ~= 180s which is the 3 minute width of the x axis.

My scenario is I'll have an unknown length of data coming (via websockets) but will know the length of time I'd like the x axis to represent. Unsure If I'll have to buffer this data to update the chart on a more regular basis.

Any insight appreciated!

@Atticweb

This comment has been minimized.

Show comment
Hide comment
@Atticweb

Atticweb Feb 26, 2016

I made a test with the last example (scroll graph) on the page: http://codepen.io/atticweb/pen/GZKvgv

I made a test with the last example (scroll graph) on the page: http://codepen.io/atticweb/pen/GZKvgv

@peterqiu1997

This comment has been minimized.

Show comment
Hide comment
@peterqiu1997

peterqiu1997 Jun 27, 2016

Hello,

The graph is working perfectly on my computer. However, if I leave the screen (such as changing tabs) and come back to it, the x-axis will speed up significantly to catch back up to the current time. Is there a way I can make it run smoothly throughout? Thank you!

Hello,

The graph is working perfectly on my computer. However, if I leave the screen (such as changing tabs) and come back to it, the x-axis will speed up significantly to catch back up to the current time. Is there a way I can make it run smoothly throughout? Thank you!

@pepijnverburg

This comment has been minimized.

Show comment
Hide comment
@pepijnverburg

pepijnverburg Apr 4, 2017

Hi,

Quick addition for people who might also have problems with the translate reset not being executed properly, because the translate transition is still in progress (might happen when you are dealing with varying frame rates or data intervals). Took me a fair while and significant debugging to figure it out.

Make sure you reset the transition on the selection when resetting the transform:

d3.select(this)
  .attr("d", line)
  .transition().duration(0)
  .attr("transform", null);

Hi,

Quick addition for people who might also have problems with the translate reset not being executed properly, because the translate transition is still in progress (might happen when you are dealing with varying frame rates or data intervals). Took me a fair while and significant debugging to figure it out.

Make sure you reset the transition on the selection when resetting the transform:

d3.select(this)
  .attr("d", line)
  .transition().duration(0)
  .attr("transform", null);
@xmesaj2

This comment has been minimized.

Show comment
Hide comment
@xmesaj2

xmesaj2 Apr 9, 2017

How to modify this example to get multi-series chart, for example by adding a 2nd data array?
Now it takes let's say [11,15,30,1,10,20] and draws a line with it and how to add an example array of [33,35,21,40,22,2] that would draw another line?

xmesaj2 commented Apr 9, 2017

How to modify this example to get multi-series chart, for example by adding a 2nd data array?
Now it takes let's say [11,15,30,1,10,20] and draws a line with it and how to add an example array of [33,35,21,40,22,2] that would draw another line?

@sampaioletti

This comment has been minimized.

Show comment
Hide comment
@sampaioletti

sampaioletti Jun 23, 2017

I forked the code pen from @Atticweb and updated to v4. Everything seems to be working properly, but I'm a little unhappy with the transition of the axis text, it doesn't respect the easing (jumpy), but the ticks transition smoothly. Any ideas?

https://codepen.io/sampaioletti/pen/eREyQZ

I'm pretty new to d3, but wanted to use this same idea in a similar project that requires v4.

(sorry if you get this twice I should have posted here in the first place probably not on bjorngi's gist)

I forked the code pen from @Atticweb and updated to v4. Everything seems to be working properly, but I'm a little unhappy with the transition of the axis text, it doesn't respect the easing (jumpy), but the ticks transition smoothly. Any ideas?

https://codepen.io/sampaioletti/pen/eREyQZ

I'm pretty new to d3, but wanted to use this same idea in a similar project that requires v4.

(sorry if you get this twice I should have posted here in the first place probably not on bjorngi's gist)

@alexsmartens

This comment has been minimized.

Show comment
Hide comment
@alexsmartens

alexsmartens Mar 24, 2018

Has anyone implemented something similar in react-native? If so, would you mind sharing your experience?

Has anyone implemented something similar in react-native? If so, would you mind sharing your experience?

@Yaweii

This comment has been minimized.

Show comment
Hide comment
@Yaweii

Yaweii May 30, 2018

@alexsmartens I am trying to build something similar in react-native as well. Did you get any chance to figure this out yet?

Yaweii commented May 30, 2018

@alexsmartens I am trying to build something similar in react-native as well. Did you get any chance to figure this out yet?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment