簡體   English   中英

如何鏈接 d3 轉換?

[英]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.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM