[英]How to chain d3 transitions?
我有一組當用戶移動滑塊時發生的動畫。 滑塊條的每個增量都會創建一個過渡。 然而,每當用戶非常快速地移動滑塊時(即他們增加滑塊的速度比過渡可以完成的更快),過渡上就會出現競爭條件,舊的會被中斷,並且動畫的“流程”很奇怪。 我希望有一系列轉換,並讓它們始終按照它們被調用的順序發生。 即下一個不會開始,直到最后一個完成。 jsfiddle (按住“a”鍵與緩慢按下幾次以查看差異)
var svg = d3.select("svg"),
margin = {top: 40, right: 40, bottom: 40, left: 40},
width = svg.attr("width") - margin.left - margin.right,
height = svg.attr("height") - margin.top - margin.bottom,
g = svg.append("g").attr("transform", "translate(" + margin.left + "," + margin.top + ")");
var y = d3.scalePoint()
.domain(d3.range(50))
.range([0, height]);
g.selectAll("circle")
.data(y.domain())
.enter().append("circle")
.attr("r", 25)
.attr("cx", 50)
.attr("cy", y);
var radius = 25;
function animate() {
radius = (radius+5)%50;
g.selectAll("circle")
.transition()
.duration(500)
.attr("r", radius);
}
document.addEventListener('keydown', e => e.code === "KeyA" ? animate() : 0);
您可以使用.on("end", callback)
來收聽動畫的結束。 由於您有 50 個正在動畫的對象,並且每個對象都將獲得該事件,因此您需要一個計數器來了解其中最后一個何時完成。
為確保所有按鍵都產生動畫,但只有在前一個按鍵完成后,您不能僅在按鍵事件上調用animate()
。 而是跟蹤必須執行多少個調用,並在觸發關鍵事件時增加該調用。 僅當該計數器為零時才調用animate()
。
請注意, animate()
調用的這種排隊也可能會產生不自然的行為:動畫可能會一直持續到最后一個關鍵事件之后很久。 為了改善用戶體驗,您可以降低持續時間參數,以便在仍有大量關鍵事件需要處理其相應的animate()
調用時動畫加速。
以下是如何調整您的代碼以完成所有這些工作:
var svg = d3.select("svg"),
margin = {top: 40, right: 40, bottom: 40, left: 40},
width = svg.attr("width") - margin.left - margin.right,
height = svg.attr("height") - margin.top - margin.bottom,
g = svg.append("g").attr("transform", "translate(" + margin.left + "," + margin.top + ")");
// Use a variable for the number of items:
var size = 50;
var y = d3.scalePoint()
.domain(d3.range(size)) // <--- use that variable
.range([0, height]);
g.selectAll("circle")
.data(y.domain())
.enter().append("circle")
.attr("r", 25)
.attr("cx", 50)
.attr("cy", y);
var radius = 25;
var count = 0; // <--- the number of queued calls of animate
function animate() {
let i = size; // <-- the number of items that will be animating
console.log("anim");
radius = (radius+5)%50;
g.selectAll("circle")
.transition()
.duration(500 / count) // <--- reduce the duration when count is high
.attr("r", radius)
.on("end", () => {
i--;
// when all objects stopped animating and more animate() calls needed:
if (i === 0 && --count > 0) animate(); // on to the next...
});
}
document.addEventListener('keydown', e =>
// only call animate when no animation is currently ongoing, else increment
e.code === "KeyA" ? count++ || animate() : 0
);
如果使用最新的 D3 版本,即 v5(順便說一句,您的代碼可以按原樣使用 v5,無需更改),您可以使用已接受的答案中的方法,但使用transition.end()
而不是transition.on("end",...)
。
與transition.on("end",...)
, transition.end()
(強調我的):
返回在每個選定元素完成轉換時解析的承諾。 如果任何元素的轉換被取消或中斷,promise 就會拒絕。
這樣,您可以刪除animate()
中的i
變量,從而節省幾行。 它成為了:
function animate() {
radius = (radius + 5) % 50;
g.selectAll("circle")
.transition()
.duration(500 / count)
.attr("r", radius)
.end()
.then(() => {
if (--count > 0) animate()
});
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.