Pausing and Resuming D3-Transitions

Pausing and resuming of d3 - transitions is not a basic functionality provided by d3. But it is possible. I'll show you how!

Basic Transition

Here is a basic transition as an example to work with:

Over the time the x-coordinate of the red circle advances with constant speed representing the time. The y-coordinate is the value which is manipulated by the transition we want to examine.
Example code for starting the transition:

    var c = d3.select("circle");
    c.attr("cy",0);
    c.transition()
        .duration( time )
        .ease( ease )
        .attr( "cy", 1 );

Pausing

The most easy part is to pause or stop a transition: Select the element that does the transition and override the currently active transition with a new blank one with duration(0).

	var c = d3.select("circle");
	c.transition()
		.duration( 0 );

Resuming

But now how do we resume the recently stopped transition? Obviously when we start the new transition it should only last for the time the stopped one would have taken. So we need to track the progression of a transition. You could look up the attr() changed by the transition and calculate the passed time. This may not work because the transition might not be linear.

Personally I set an unused attribute (e.g. "T") of another element to zero at the begin and transition it linearly to one.


	var e = d3.select("#time");
	e.attr("T",0);
	c.transition()
		.duration( time )
		.ease( "linear" )
		.attr("T",1);

Now you can look up the progress of the transition at any time and we can calculate the time that is leftover.

Now lets try to restart the original transition with the left duration.

As you can see this works only for linear easing. To get proper results we have to adapt the easing function.

First we map the x-coordinate from 0..1 in the resumed transition to elapsed_time..1 in the original transition. Then we can pass this to the original easing function f(x). The results of this must again be mapped; from f(x)..1 to 0..1.

The following function does exactly this:

function resumed_ease( ease, elapsed_time ) {
    var y = typeof ease == "function" ? ease : d3.ease.call(d3, ease);
    return function( x_resumed ) {
        var x_original = d3.scale
                        .linear()
                        .domain([0,1])
                        .range([elapsed_time,1])
                        ( x_resumed );
        return d3.scale
                 .linear()
                 .domain([ y(elapsed_time), 1 ])
                 .range([0,1])
                 ( y ( x_original ) );
    };
}

Let's see how this works:

Voilą!

Now you know how to pause and resume transitions in d3! Congratulations