【发布时间】:2012-05-28 08:35:18
【问题描述】:
我需要使用D3.js 创建一个 FadeOut 方法(类似于 jQuery)。我需要做的是使用transition() 将不透明度设置为0。
d3.select("#myid").transition().style("opacity", "0");
问题是我需要一个回调来实现转换何时完成。如何实现回调?
【问题讨论】:
标签: javascript d3.js transition
我需要使用D3.js 创建一个 FadeOut 方法(类似于 jQuery)。我需要做的是使用transition() 将不透明度设置为0。
d3.select("#myid").transition().style("opacity", "0");
问题是我需要一个回调来实现转换何时完成。如何实现回调?
【问题讨论】:
标签: javascript d3.js 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);
来自transition.each([type],listener) 的文档:
如果指定了 type,则为过渡事件添加监听器,同时支持“开始”和“结束”事件。将为过渡中的每个单独元素调用侦听器,即使过渡具有恒定的延迟和持续时间。 start 事件可用于在每个元素开始转换时触发瞬时变化。结束事件可用于通过选择当前元素
this并派生新的转换来启动多阶段转换。在结束事件期间创建的任何过渡都将继承当前的过渡 ID,因此不会覆盖之前安排的较新过渡。
详情请见this forum thread on the topic。
最后,请注意,如果您只想在元素淡出后(过渡完成后)移除元素,您可以使用transition.remove()。
【讨论】:
d3.selectAll() 的所有 转换完成后(而不是在每个元素完成后)执行回调?换句话说,我只想在所有元素完成转换后回调一个函数。
.each 事件侦听器,也不使用 "end" 事件。它似乎没有“链接”转换。第二个链接指向一个不适合我的 github。
Mike Bostock 的 solution 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") });
【讨论】:
if (transition.size() === 0) { callback(); }
function endall(transition, callback){ if(!callback) return; // ... } 或者,因为调用它肯定是一个错误这个函数没有回调,抛出异常接缝是处理这种情况的合适方法我认为这种情况不需要太复杂的异常function endall(transition, callback){ if(!callback) throw "Missing callback argument!"; // .. }
enter() 和exit() 转换,并且想等到所有三个都完成时,我们需要在回调中放入代码以确保它被调用了三次,对吧? D3太乱了!我希望我选择了另一个图书馆。
现在,在 d3 v4.0 中,有一个工具可以将事件处理程序显式附加到转换:
https://github.com/d3/d3-transition#transition_on
要在转换完成后执行代码,您只需要:
d3.select("#myid").transition().style("opacity", "0").on("end", myCallback);
【讨论】:
transition.remove() (link),它处理从视图中转换元素的常见用例:`"对于每个选定的元素,在转换结束时移除元素,只要因为该元素没有其他活动或待处理的转换。如果该元素有其他活动或待处理的转换,则不执行任何操作。"
一种稍微不同的方法,也适用于同时运行多个元素的多个转换:
var transitions = 0;
d3.select("#myid").transition().style("opacity","0").each( "start", function() {
transitions++;
}).each( "end", function() {
if( --transitions === 0 ) {
callbackWhenAllIsDone();
}
});
【讨论】:
以下是 Mike Bostock 的 solution 的另一个版本,灵感来自 @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;
}
使用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,即使transition 为空如果指定毫秒数或者如果第二个参数是真实的。这也会将任何其他参数转发给callback(并且只有那些参数)。重要的是,如果transition 为空,这将默认情况下不会应用callback,在这种情况下这可能是一个更安全的假设。
【讨论】:
从 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();
查看版本发布说明了解更多信息:
【讨论】:
Mike Bostock 的 solution 改进了 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");
【讨论】:
实际上还有另一种使用计时器的方法。
var timer = null,
timerFunc = function () {
doSomethingAfterTransitionEnds();
};
transition
.each("end", function() {
clearTimeout(timer);
timer = setTimeout(timerFunc, 100);
});
【讨论】:
我通过使用变量设置转换的持续时间解决了类似的问题。然后我用setTimeout() 调用了下一个函数。就我而言,我希望转换和下一次调用之间有轻微的重叠,正如您将在我的示例中看到的那样:
var transitionDuration = 400;
selectedItems.transition().duration(transitionDuration).style("opacity", .5);
setTimeout(function () {
sortControl.forceSort();
}, (transitionDuration * 0.75));
【讨论】: