Invoke a callback at the end of a transition

2019-01-03 13:51发布

I need to make a FadeOut method (similar to jQuery) using D3.js. What I need to do is to set the opacity to 0 using transition().

d3.select("#myid").transition().style("opacity", "0");

The problem is that I need a callback to realize when the transition has finished. How can I implement a callback?

8条回答
▲ chillily
2楼-- · 2019-01-03 13:51

Now, in d3 v4.0, there is a facility for explicitly attaching event handlers to transitions:

https://github.com/d3/d3-transition#transition_on

To execute code when a transition has completed, all you need is:

d3.select("#myid").transition().style("opacity", "0").on("end", myCallback);
查看更多
做个烂人
3楼-- · 2019-01-03 13:55

Mike Bostock's solution for v3 with a small update:

  function endall(transition, callback) { 
    if (typeof callback !== "function") throw new Error("Wrong callback in endall");
    if (transition.size() === 0) { callback() }
    var n = 0; 
    transition 
        .each(function() { ++n; }) 
        .each("end", function() { if (!--n) callback.apply(this, arguments); }); 
  } 

  d3.selectAll("g").transition().call(endall, function() { console.log("all done") });
查看更多
Lonely孤独者°
4楼-- · 2019-01-03 13:57

You want to listen for the "end" event of the transition.

d3.select("#myid").transition().style("opacity","0").each("end", myCallback);
  • This demo uses the "end" event to chain many transitions in order.
  • The donut example that ships with D3 also uses this to chain together multiple transitions.
  • Here's my own demo that changes the style of elements at the start and end of the transition.

From the documentation for transition.each([type],listener):

If type is specified, adds a listener for transition events, supporting both "start" and "end" events. The listener will be invoked for each individual element in the transition, even if the transition has a constant delay and duration. The start event can be used to trigger an instantaneous change as each element starts to transition. The end event can be used to initiate multi-stage transitions by selecting the current element, this, and deriving a new transition. Any transitions created during the end event will inherit the current transition ID, and thus will not override a newer transition that was previously scheduled.

See this forum thread on the topic for more details.

Finally, note that if you just want to remove the elements after they have faded out (after the transition has finished), you can use transition.remove().

查看更多
啃猪蹄的小仙女
5楼-- · 2019-01-03 13:57

Mike Bostock's solution improved by kashesandr + passing arguments to the callback function:

function d3_transition_endall(transition, callback, arguments) {
    if (!callback) callback = function(){};
    if (transition.size() === 0) {
        callback(arguments);
    }

    var n = 0;
    transition
        .each(function() {
            ++n;
        })
        .each("end", function() {
            if (!--n) callback.apply(this, arguments);
    });
}

function callback_function(arguments) {
        console.log("all done");
        console.log(arguments);
}

d3.selectAll("g").transition()
    .call(d3_transition_endall, callback_function, "some arguments");
查看更多
小情绪 Triste *
6楼-- · 2019-01-03 14:01

A slightly different approach that works also when there are many transitions with many elements each running simultaneously:

var transitions = 0;

d3.select("#myid").transition().style("opacity","0").each( "start", function() {
        transitions++;
    }).each( "end", function() {
        if( --transitions === 0 ) {
            callbackWhenAllIsDone();
        }
    });
查看更多
爷、活的狠高调
7楼-- · 2019-01-03 14:01

Actually there's one more way to do this using timers.

var timer = null,
    timerFunc = function () {
      doSomethingAfterTransitionEnds();
    };

transition
  .each("end", function() {
    clearTimeout(timer);
    timer = setTimeout(timerFunc, 100);
  });
查看更多
登录 后发表回答