Related d3/d3#1576.
Consider the looped transition from bl.ocks.org/7df5eb012ff76589419d:
selection.transition()
.duration(2500)
.delay(function(d) { return d * 40; })
.each("start", slideRight);
function slideRight() {
d3.activeTransition(this)
.attr("cx", width)
.transition()
.each("start", slideLeft);
}
function slideLeft() {
d3.activeTransition(this)
.attr("cx", 0)
.transition()
.each("start", slideRight);
}
Say the page is moved to a background tab during a slide-right transition, which we will call transition A. If the page is returned to the foreground after five or more seconds (any delay greater than twice the transition duration), then transition A immediately ends, setting all the circles to the far right in its last tick. During the same frame, since it was previously scheduled at the start of transition A, a slide-left transition B then starts. A new slide-right transition C is also scheduled to follow B.
The problem is that when the slide-left transition B starts, its first (and only) tick—when it would set the circles to the left—is deferred to the end of the current frame due to the optimization in d3/d3#1576. Thus, the slide-right transition C which was intended to run after B instead starts and initializes its tweens before transition B gets its only tick. And thus the slide-right transition C may be a no-op:
Note that if we change the code ever-so-slightly to use end events rather than start events, the inversion does not occur:
select.transition()
.duration(2500)
.delay(function(d) { return d * 40; })
.attr("cx", width)
.each("end", slideLeft);
function slideLeft() {
d3.activeTransition(this).transition()
.attr("cx", 0)
.each("end", slideRight);
}
function slideRight() {
d3.activeTransition(this).transition()
.attr("cx", width)
.each("end", slideLeft);
}
In this case, when the page is paused and resumed, the slide-right transition A immediately ends, setting all the circles to the far right. Then the slide-left transition B is scheduled and starts at the end of the current frame. When the slide-left transition B starts, it initializes its tweens and schedules its first tick again at the end of the current frame. The tick is then invoked, setting the circles to the far left, and transition B dispatches an end event. Then, a new slide-right transition C is scheduled and starts, again at the end of the current frame. Thus, the relative ordering is correctly preserved.