D3 V4使用带有合并的常规更新模式进入元素时进行转换

时间:2018-04-07 13:13:47

标签: javascript d3.js transition

据我所知,不可能在standard enter/append/merge chain中包含对输入元素的转换,因为这样做会将输入元素选择替换为无法与更新选择合并的转换。 (关于选择和转换之间的区别,请参阅here。)

(针对评论编辑的问题)

如果所需的效果是顺序转换,合并之前和合并之后,可以按如下方式完成:

// Join data, store update selection      
      circ = svg.selectAll("circle")
          .data(dataset);

// Add new circle and store entering circle selection        
      var newcirc = circ.enter().append("circle")
         *attributes*

// Entering circle transition        
      newcirc    
          .transition()
          .duration(1000)
          *modify attributes*
          .on("end", function () {

// Merge entering circle with existing circles, transition all        
      circ = newcirc.merge(circ)
          .transition()
          .duration(1000)
          *modify attributes*
      });

jsfiddle

我想知道是否有办法在不破坏输入/追加/合并链的情况下执行此操作。

1 个答案:

答案 0 :(得分:4)

毫无疑问,您必须在方法链中至少有一个中断,因为您需要保留对更新选择的引用,以便稍后可以将其合并到输入选择中。如果你对此感觉不错,有一种方法可以在最初的休息之后保持链完好无损。

我列出了在answer"Can you chain a function after a transition without it being a part of the transition?"工作的基本原则。这使用transition.selection(),它允许您从当前转换中解脱出来并访问启动转换的基础选择。但是,您的代码更复杂,因为链式转换会增加复杂性。

第一部分是像以前一样存储更新选择:

// Join data, store update selection      
const circUpd = svg.selectAll("circle")
  .data(dataset);

第二个不间断的部分是这样的:

const circ = circUpd              // 2. Store merged selection from 1.
  .enter().append("circle")
    // .attr()...
  .transition()
    // .duration(1000)
    // .attr()...
    .on("end", function () {
      circ.transition()           // 3. Use merged selection from 2.
        // .duration(1000)
        // .attr()...
    })
    .selection().merge(circUpd);  // 1. Merge stored update into enter selection.

这可能需要对上面编号的步骤进行一些进一步的解释:

  1. 最后一行是最重要的一行 - 在开始转换之后,代码使用.selection()来保持转换所基于的选择,即输入选择,进而选择可以用来轻松地将存储的更新选择合并到其中。

  2. 包含输入和更新选择的合并选择是整个链的结果,然后存储在circ

  3. 这是棘手的部分!重要的是要理解,提供给.on("end", function() {...})的函数是一个在转换结束之前不执行的回调。虽然此行在之前选择合并,但它实际上是在合并之后执行的。但是,通过引用circ,它会关闭过度捕获,如果您愿意,则会引用circ。这样,当实际执行回调时,circ已经引用先前合并的选择。

  4. 请查看以下工作代码段:

    
    
    var w = 250;
    var h = 250;
              
    // Create SVG          
    var svg = d3.select("body")
      .append("svg")
        .attr("width", w)
        .attr("height", h);
    
    // Create background rectangle          
    svg.append("rect")
      .attr("x", "0")
      .attr("y", "0")
      .attr("width", w)
      .attr("height", h)
      .attr("fill", "aliceblue");
    
    var dataset = [170, 220, 40, 120, 0, 300];
    
    var xScale = d3.scaleBand()
      .domain(d3.range(dataset.length))
      .range([0, w]);
          
    var yScale = d3.scaleLinear()
      .domain([0, 300])
      .range([75, 200])
      
    var rad = xScale.bandwidth()/2
      
    // Join data  
    var circ = svg.selectAll("circle")
      .data(dataset);
    
    // Create initial circles
    circ.enter().append("circle")
        .attr("cx", (d, i) => xScale(i)+rad)
        .attr("cy", d => yScale(d))
        .attr("r", rad)
        .attr("fill", "blue");
    
    // Trigger update on click
    d3.select("h3")
      .on("click", function () {
    
        // Create new data value        
        var newvalue = Math.floor(Math.random()*300);
            
        dataset.push(newvalue);
    
        xScale.domain(d3.range(dataset.length));
            
        rad = xScale.bandwidth()/2;
     
        // Join data, store update selection      
        const circUpd = svg.selectAll("circle")
          .data(dataset);
        
        // Add new circle and store entering circle selection        
        const circ = circUpd              // 2. Store merged selection from 1.
          .enter().append("circle")
            .attr("cx", "0")
            .attr("cy", "25")
            .attr("r", rad)
            .attr("fill", "red")
          .transition()
            .duration(1000)
            .attr("cx", (d, i) => xScale(i)+rad)
            .on("end", function () {
              circ.transition()           // 3. Use merged selection from 2.
                  .duration(1000)
                  .attr("cx", (d, i) => xScale(i)+rad)
                  .attr("cy", d => yScale(d))
                  .attr("r", rad)
                  .attr("fill", "blue");
      	    })
          .selection().merge(circUpd);  // 1. Merge stored update into enter selection.
    
    });
    
    <script src="https://d3js.org/d3.v4.js"></script>
    <h3>Add a circle</h3>
    &#13;
    &#13;
    &#13;