如果满足条件,则在另一个过渡期间添加并发过渡

Jag*_*iru 5 html javascript css d3.js

我试图在过渡运行时添加一个新过渡,条件是如果bar1宽度与bar2匹配,则钢筋会改变位置。

我已经使用transition()。tween查看是否满足条件。当第二个转换开始时,第一个停止。我希望第一个过渡可以继续运行,直到其持续时间结束为止,即使第二个过渡已经开始。

我有代码,但无法在第二个过程中继续第一个过渡。请帮忙。

window.i1 = 0;
window.i2 = 0;

var svg = d3.select("body")
   .append("svg")
   .attr("width", 500)
   .attr("height", 500);

var bar1 = svg.append("rect")
   .attr("fill", "green")
   .attr("x", 20)
   .attr("y", 40)
   .attr("height", 20)
   .attr("width", 40)

var bar2 = svg.append("rect")
   .attr("fill", "blue")
   .attr("x", 20)
   .attr("y", 70)
   .attr("height", 20)
   .attr("width", 20)

update();

function update() {
   bar1.transition()
     .ease(d3.easeLinear)
     .duration(2000)
     .attr("width",100)
     .tween("attr.fill", function() {
        var node = this;
        return function(t) {
         window.bar1width = node.getAttribute("width");
         var bl = check();

         if(bl=="true"&&window.i1==0){

            chnPos(); 
           window.i1=window.i1+1;
         }
       }
      })
 

   bar2.transition()
     .ease(d3.easeLinear)
     .duration(2000)
     .attr("width",120)
     .tween("attr.fill", function() {
        var node = this;
        return function(t) {
          window.bar2width = node.getAttribute("width");
          var bl = check();
          if(bl=="true"&&window.i2==0){
          chnPos();
          window.i2=window.i2+1;
        }
       }
      })
    }

function check() {
 if (window.bar2width>=window.bar1width){
   console.log(window.bar1width +' ' + window.bar2width);
   return "true";
 }
 //console.log(true)
return "false";

}

function chnPos(){
    bar1.transition()
      .ease(d3.easeLinear)
      .duration(500)
      .attr("y",70)
    bar2.transition()
      .ease(d3.easeLinear)
      .duration(500)
      .attr("y",40)
}
Run Code Online (Sandbox Code Playgroud)
<!DOCTYPE html>
<html>
  <head>
    <script src="https://cdnjs.cloudflare.com/ajax/libs/d3/5.3.0/d3.min.js"></script>
  </head>
  <body>

      <script type="text/javascript" src="index.js"></script>
      </body>
</html>
Run Code Online (Sandbox Code Playgroud)

And*_*eid 6

在 d3v4+ 中,您可以有多个并发转换,但它们需要有单独的名称:

选择.transition([名称]) <>

返回具有指定名称的给定选择的新转换。如果未指定名称,则使用 null。新过渡仅与同名的其他过渡互斥。(文档

让我们为过渡添加一些名称,我在下面使用“grow”和“switch”

window.i1 = 0;
window.i2 = 0;

var svg = d3.select("body")
   .append("svg")
   .attr("width", 500)
   .attr("height", 500);


var bar1 = svg.append("rect")
   .attr("fill", "green")
   .attr("x", 20)
   .attr("y", 40)
   .attr("height", 20)
   .attr("width", 40)

var bar2 = svg.append("rect")
   .attr("fill", "blue")
   .attr("x", 20)
   .attr("y", 70)
   .attr("height", 20)
   .attr("width", 20)
   
   update();

function update() {
    bar1.transition("grow")
       .ease(d3.easeLinear)
       .duration(2000)
       .attr("width",100)
       .tween("attr.fill", function() {
         var node = this;
           return function(t) {
             window.bar1width = node.getAttribute("width");
         var bl = check();

         if(bl=="true"&&window.i1==0){

         chnPos(); 
         window.i1=window.i1+1;
      }
    }
  })

  bar2.transition("grow")
    .ease(d3.easeLinear)
    .duration(2000)
    .attr("width",120)
    .tween("attr.fill", function() {
       var node = this;
         return function(t) {
            window.bar2width = node.getAttribute("width");
            var bl = check();
            if(bl=="true"&&window.i2==0){
              chnPos();
              window.i2=window.i2+1;
            }
          }
     })
 }

function check() {
 if (window.bar2width>=window.bar1width){
   //console.log(window.bar1width +' ' + window.bar2width);
   return "true";
 }
 //console.log(true)
return "false";

}

function chnPos(){
bar1.transition("switch")
      .ease(d3.easeLinear)
      .duration(500)
      .attr("y",70)
bar2.transition("switch")
      .ease(d3.easeLinear)
      .duration(500)
      .attr("y",40)

}
Run Code Online (Sandbox Code Playgroud)
<!DOCTYPE html>
<html>
  <head>
    <script src="https://cdnjs.cloudflare.com/ajax/libs/d3/5.3.0/d3.min.js"></script>
  </head>
  <body>

      <script type="text/javascript" src="index.js"></script>
      </body>
</html>
Run Code Online (Sandbox Code Playgroud)

我只想补充一点,这可能可以稍微简化 - 因为为每个元素单独创建转换的方法引入了很多额外的代码。每增加一个栏,代码的复杂性也会增加一点。您应该能够使用绑定数据和一些排序来对长度转换期间具有转换的元素进行重新排序。也许是这样的(这是一个粗略的片段,肯定有更好的方法):

var data = [
 { start:200, current: 200, end: 40 },
 { start:120, current: 120, end: 240 },
 { start:10, current: 10, end: 260 }
];
var colors =["crimson","steelblue","lawngreen","orange"];

var svg = d3.select("body")
   .append("svg")
   .attr("width", 500)
   .attr("height", 500);
   
var bars = svg.selectAll("rect")
  .data(data)
  .enter()
  .append("rect")
  .attr("x", 20)
  .attr("y", function(d,i) { return i*30+20; })
  .attr("width", function(d) { return d.start; })
  .attr("height", 20)
  .attr("fill",function(d,i) { return colors[i]; })
  .on("click", order);


 
bars.transition("length")
  .attr("width", function(d) { return d.end; })
  .tween("attr.current", function(d,i) {
    var bar = d3.select(this);
	  var that = this;
    return function() { 
      d.current = +bar.attr("width");
      bars = bars.sort(function(a,b) { 
        return b.current - a.current; 
      }).order();
	  // trigger new transition if needed:
	  var nodes = bars.nodes();
	  if(nodes[i] != that) {
	     for(var j = 0; j < nodes.length; j++) {
		   if(nodes[j] == that) { i=j; break;}
		 }
	     order();
      }
    }
  })
  .duration(4000);

function order(bar) {
  bars.transition("order")
    .attr("y", function(d,i) { return i*30+20; })
	//.ease(d3.easeLinear)
}
Run Code Online (Sandbox Code Playgroud)
<!DOCTYPE html>
<html>
  <head>
    <script src="https://cdnjs.cloudflare.com/ajax/libs/d3/5.3.0/d3.min.js"></script>
  </head>
  <body>

      <script type="text/javascript" src="index.js"></script>
      </body>
</html>
Run Code Online (Sandbox Code Playgroud)


为了获得更多解释,我将分解第二个片段的主要转换:

// Transition each bar's width/length:
bars.transition("length")

  // set the final width value:
  .attr("width", function(d) { return d.end; })

  // Modify the datum throughout the transition
  // This function is called once for each element
  // This means we need to update d,i manually during the transition
  .tween("attr.current", function(d,i) {

    // Keep track of an individual bar being transitioned (element & selection):
    var bar = d3.select(this);
    var that = this;

    // This function is invoked each tick for each bar:
    return function() { 
      // Update a bar's datum to reflect current width: 
      d.current = +bar.attr("width");
      // Sort the bars based on current width:
      bars = bars.sort(function(a,b) { 
        return b.current - a.current; 
      })
      .order(); // Pull up the longest bar so it is drawn last (if there is overlap, it will be on top)

      // trigger new transition if needed:
      // Has the bar being transitioned been moved in the selection? 
      // If so, nodes[i] will not equal the element being moved (that)
      var nodes = bars.nodes();
      if(nodes[i] != that) {
         // If it has been moved, update i to reflect the element's new index
         for(var j = 0; j < nodes.length; j++) {
           if(nodes[j] == that) { i=j; break;}
         }
         // And apply the transition on the vertical spacing:
         order();
      }
    }
  })
  .duration(4000);
Run Code Online (Sandbox Code Playgroud)

如果不检查节点顺序是否已更改,则将重复触发第二个转换,替换之前的第二个转换。最明显的结果是默认使用 d3.easeCubic:过渡的开始很慢。如果不断重新启动第二个转换,则在第一个转换完成之前,第二个转换永远不会移动得很快。这也可能是上述代码片段的问题,但前提是快速连续发生大量位置变化。