d3 / d3-transition

Animated transitions for D3 selections.
https://d3js.org/d3-transition
ISC License
223 stars 65 forks source link

Transition "too late" error despite interrupt: #104

Closed richburdon closed 4 years ago

richburdon commented 4 years ago

In d3 5.9.2 d3-transition 1.3.2

Re: https://github.com/d3/d3-transition/issues/63

Probably my misunderstanding of life-cycles but to my reading interrupt should immediately cancel pending transitions with the same id. However in my example if the timer frequency is shorter than the transition duration, then the error is thrown. (I would also have expected interrupt to be optional here since the transition instance/ID is the same).

const t = d3.transition('xxx')
  .duration(500)

d3.interval(elapsed => {
  d3.select(group)
    .interrupt(t)
    .transition(t)
    .selectAll('circle')
    .attr('r', 100 - Math.log(elapsed) * 10);
}, 100) // OK if > 500

The work-arounds are either to catch the exception, or create a separate "scheduler" to create the new transition after the "end" event, which seems clunky.

mbostock commented 4 years ago

I’m not able to reproduce this error based on the provided snippet. Please fork the notebook below.

https://observablehq.com/d/3d3d5b3ddc9fa71c

That said, it looks like you’re creating a single transition t and then repeatedly calling selection.transition(t) into the future. This won’t work because the original transition will have completed; you need to create a new transition if you want a new transition rather than to change the parameters of an existing transition.

Also, selection.interrupt(…) takes a name, not a transition instance, so your code is equivalent to selection.interrupt("[object Object]"). You likely mean to say selection.interrupt("xxx"). Also, note that selection.interrupt only interrupts the selected elements, not descendant elements who may also be transition. You need to select every element you want to interrupt.

richburdon commented 4 years ago

Thanks for the immediate response.

Calling interrupt with the name solved the issue. Although I had tried interrupt with no params and had expected that to clear everything.