Javascript 在转换结束时调用回调
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/10692100/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
Invoke a callback at the end of a transition
提问by Tony
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.js制作一个 FadeOut 方法(类似于 jQuery)。我需要做的是使用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?
问题是我需要一个回调来实现转换完成的时间。如何实现回调?
回答by Phrogz
You want to listen for the "end" event of the transition.
您想监听转换的“结束”事件。
// d3 v5
d3.select("#myid").transition().style("opacity","0").on("end", myCallback);
// old way
d3.select("#myid").transition().style("opacity","0").each("end", myCallback);
- This demouses the "end" event to chain many transitions in order.
- The donut examplethat ships with D3 also uses this to chain together multiple transitions.
- Here's my own demothat changes the style of elements at the start and end of the transition.
From the documentation for transition.each([type],listener)
:
从文档中transition.each([type],listener)
:
If typeis 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.
如果指定了类型,则为转换事件添加一个侦听器,同时支持“开始”和“结束”事件。将为转换中的每个单独元素调用侦听器,即使转换具有恒定的延迟和持续时间。start 事件可用于在每个元素开始转换时触发瞬时变化。结束事件可用于通过选择当前元素
this
和派生新转换来启动多阶段转换。在结束事件期间创建的任何过渡都将继承当前的过渡 ID,因此不会覆盖先前安排的较新的过渡。
See this forum thread on the topicfor 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()
.
最后,请注意,如果您只想在元素淡出后(过渡完成后)删除它们,您可以使用transition.remove()
.
回答by kashesandr
Mike Bostock's solutionfor v3with a small update:
迈克·博斯托克的解决方案为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") });
回答by ericsoco
Now, in d3 v4.0, there is a facility for explicitly attaching event handlers to transitions:
现在,在 d3 v4.0 中,有一个工具可以显式地将事件处理程序附加到转换:
https://github.com/d3/d3-transition#transition_on
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);
回答by Jesper We
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();
}
});
回答by milos
The following is another version of Mike Bostock's solutionand inspired by @hughes' comment to @kashesandr's answer. It makes a single callback upon transition
's end.
以下是 Mike Bostock解决方案的另一个版本,其灵感来自 @hughes 对 @kashesandr 回答的评论。它在transition
's 结束时进行一次回调。
Given a drop
function...
给定一个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);
}
... we can extend d3
like so:
...我们可以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;
}
Use transition.end(callback[, delayIfEmpty[, arguments...]])
:
使用transition.end(callback[, delayIfEmpty[, arguments...]])
:
transition.end(function() {
console.log("all done");
});
... or with an optional delay if transition
is empty:
... 或者如果transition
为空则有一个可选的延迟:
transition.end(function() {
console.log("all done");
}, 1000);
... or with optional callback
arguments:
...或带有可选callback
参数:
transition.end(function(x) {
console.log("all done " + x);
}, 1000, "with callback arguments");
d3.transition.end
will apply the passed callback
even with an empty transition
ifthe number of milliseconds is specified or ifthe second argument is truthy. This will also forward any additional arguments to the callback
(and only those arguments). Importantly, this will not by defaultapply the callback
if transition
is empty, which is probably a safer assumption in such a case.
d3.transition.end
如果指定了毫秒数或第二个参数为真,callback
即使为空,也将应用传递的值。这也会将任何其他参数转发给(并且仅这些参数)。重要的是,默认情况下这不会应用if为空,在这种情况下这可能是一个更安全的假设。transition
callback
callback
transition
回答by chrismarx
As of D3 v5.8.0+, there is now an official way to do this using transition.end
. The docs are here:
从 D3 v5.8.0+ 开始,现在有一种使用transition.end
. 文档在这里:
https://github.com/d3/d3-transition#transition_end
https://github.com/d3/d3-transition#transition_end
A working example from Bostock is here:
Bostock 的一个工作示例如下:
https://observablehq.com/@d3/transition-end
https://observablehq.com/@d3/transition-end
And the basic idea is that just by appending .end()
, the transition will return a promise that won't resolve until all elements are done transitioning:
基本思想是,仅通过附加.end()
,转换将返回一个承诺,直到所有元素都完成转换后才会解决:
await d3.selectAll("circle").transition()
.duration(1000)
.ease(d3.easeBounce)
.attr("fill", "yellow")
.attr("cx", r)
.end();
See the version release notes for even more:
有关更多信息,请参阅版本发行说明:
回答by int_ua
Mike Bostock's solutionimproved by kashesandr+ passing arguments to the callback function:
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");
回答by Brett
I solved a similar problem by setting a duration on transitions using a variable. Then I used setTimeout()
to call the next function. In my case, I wanted a slight overlap between the transition and the next call, as you'll see in my example:
我通过使用变量设置转换的持续时间解决了类似的问题。然后我用来setTimeout()
调用下一个函数。就我而言,我希望转换和下一次调用之间有轻微的重叠,如您在我的示例中所见:
var transitionDuration = 400;
selectedItems.transition().duration(transitionDuration).style("opacity", .5);
setTimeout(function () {
sortControl.forceSort();
}, (transitionDuration * 0.75));
回答by ifadey
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);
});