101

我需要使用D3.js制作一个 FadeOut 方法(类似于 jQuery)。我需要做的是使用将不透明度设置为 0 transition()

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

问题是我需要一个回调来实现转换何时完成。如何实现回调?

4

9 回答 9

149

您想监听过渡的“结束”事件。

// d3 v5
d3.select("#myid").transition().style("opacity","0").on("end", myCallback);

// old way
d3.select("#myid").transition().style("opacity","0").each("end", myCallback);
  • 此演示使用“结束”事件按顺序链接许多转换。
  • D3 附带的甜甜圈示例也使用它来将多个转换链接在一起。
  • 这是我自己的演示,它改变了过渡开始和结束时元素的样式。

从文档中transition.each([type],listener)

如果指定了类型,则为转换事件添加一个监听器,同时支持“开始”和“结束”事件。将为过渡中的每个单独元素调用侦听器,即使过渡具有恒定的延迟和持续时间。start 事件可用于在每个元素开始转换时触发瞬时变化。结束事件可用于通过选择当前元素 来启动多阶段转换this,并派生新的转换。在结束事件期间创建的任何过渡都将继承当前的过渡 ID,因此不会覆盖先前计划的较新过渡。

有关更多详细信息,请参阅有关该主题的此论坛主题

最后,请注意,如果您只想在元素淡出后(过渡完成后)删除元素,您可以使用transition.remove().

于 2012-05-21T20:33:37.153 回答
66

Mike Bostock 的v3解决方案有一个小的更新:

  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") });
于 2013-12-25T14:35:13.883 回答
44

现在,在 d3 v4.0 中,有一个工具可以将事件处理程序显式附加到转换:

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

要在转换完成后执行代码,您只需要:

d3.select("#myid").transition().style("opacity", "0").on("end", myCallback);
于 2016-07-23T03:54:47.040 回答
10

一种稍微不同的方法也适用于有许多元素同时运行的许多转换:

var transitions = 0;

d3.select("#myid").transition().style("opacity","0").each( "start", function() {
        transitions++;
    }).each( "end", function() {
        if( --transitions === 0 ) {
            callbackWhenAllIsDone();
        }
    });
于 2014-07-24T19:23:34.373 回答
6

以下是 Mike Bostock解决方案的另一个版本,灵感来自 @hughes 对 @kashesandr 的回答的评论。transition它在' 结束时进行单个回调。

给定一个drop函数...

function drop(n, args, callback) {
    for (var i = 0; i < args.length - n; ++i) args[i] = args[i + n];
    args.length = args.length - n;
    callback.apply(this, args);
}

...我们可以d3像这样扩展:

d3.transition.prototype.end = function(callback, delayIfEmpty) {
    var f = callback, 
        delay = delayIfEmpty,
        transition = this;

    drop(2, arguments, function() {
        var args = arguments;
        if (!transition.size() && (delay || delay === 0)) { // if empty
            d3.timer(function() {
                f.apply(transition, args);
                return true;
            }, typeof(delay) === "number" ? delay : 0);
        } else {                                            // else Mike Bostock's routine
            var n = 0; 
            transition.each(function() { ++n; }) 
                .each("end", function() { 
                    if (!--n) f.apply(transition, args); 
                });
        }
    });

    return transition;
}

作为一个 JSFiddle

使用transition.end(callback[, delayIfEmpty[, arguments...]])

transition.end(function() {
    console.log("all done");
});

...或者如果transition为空,则可选延迟:

transition.end(function() {
    console.log("all done");
}, 1000);

...或带有可选callback参数:

transition.end(function(x) {
    console.log("all done " + x);
}, 1000, "with callback arguments");

d3.transition.end如果指定了毫秒数第二个参数为真,则callback即使为空也会应用传递的参数。这也会将任何额外的参数转发给(并且只有那些参数)。重要的是,默认情况下这不会应用if为空,在这种情况下这可能是一个更安全的假设。transition callbackcallbacktransition

于 2015-05-28T13:03:11.060 回答
5

从 D3 v5.8.0+ 开始,现在有一种官方方法可以使用transition.end. 文档在这里:

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

Bostock 的一个工作示例如下:

https://observablehq.com/@d3/transition-end

基本思想是,仅通过附加.end(),转换将返回一个在所有元素都完成转换之前不会解析的承诺:

 await d3.selectAll("circle").transition()
      .duration(1000)
      .ease(d3.easeBounce)
      .attr("fill", "yellow")
      .attr("cx", r)
    .end();

有关更多信息,请参阅版本发行说明:

https://github.com/d3/d3/releases/tag/v5.8.0

于 2019-09-04T22:12:58.927 回答
0

Mike Bostock 的解决方案通过kashesandr + 将参数传递给回调函数进行了改进:

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");
于 2017-01-24T18:08:52.817 回答
-2

我通过使用变量设置转换的持续时间解决了类似的问题。然后我用来setTimeout()调用下一个函数。就我而言,我希望转换和下一次调用之间有轻微的重叠,正如您将在我的示例中看到的那样:

var transitionDuration = 400;

selectedItems.transition().duration(transitionDuration).style("opacity", .5);

setTimeout(function () {
  sortControl.forceSort();
}, (transitionDuration * 0.75)); 
于 2015-02-02T18:22:22.883 回答
-2

实际上,还有另一种方法可以使用计时器来做到这一点。

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

transition
  .each("end", function() {
    clearTimeout(timer);
    timer = setTimeout(timerFunc, 100);
  });
于 2014-05-21T13:10:49.807 回答