d3.js - v3 和 v4 - 输入和更新差异

d3.js - v3 and v4 - Enter and Update differences

我正在尝试获取 xy 的值以使用 d3.js v4 制作圆圈。使用以下代码,我设法创建了类似于圆圈行为的图表,但是当我尝试 运行 v4 中的相同代码时,它不再起作用。我知道 v4 的更新有一些差异,但我没有找到任何相关信息。所以我想知道是否有人可以帮助我 运行 d3.js v4.

中的这段代码

这是使用 v3 的代码(使用 v4 会中断):

var svg = d3.select('body').append('svg')
  .attr('width', 250)
  .attr('height', 250);

//render the data
function render(data) {
  //Bind 
  var circles = svg.selectAll('circle').data(data);

  //Enter
  circles.enter().append('circle')
    .attr('r', 10);
  //Update
  circles
    .attr('cx', function(d) {
      return d.x;
    })
    .attr('cy', function(d) {
      return d.y;
    });


  //Exit
  circles.exit().remove();
}



var myObjects = [{
  x: 100,
  y: 100
}, {
  x: 130,
  y: 120
}, {
  x: 80,
  y: 180
}, {
  x: 180,
  y: 80
}, {
  x: 180,
  y: 40
}];


render(myObjects);
<script src='https://d3js.org/d3.v3.min.js'></script>

d3v4 中的更新模式已更改。 摘自documentation

In addition, selection.append no longer merges entering nodes into the update selection; use selection.merge to combine enter and update after a data join.

你应该这样重写你的代码:

var svg = d3.select('body').append('svg')
            .attr('width', 250)
            .attr('height', 250);

    //render the data
    function render(data){
            //Bind 
            var circles = svg.selectAll('circle').data(data);

            //Enter
            circles.enter().append('circle')
                .attr('r', 10).merge(circles) // <== !!!
                .attr('cx', function(d) { return d.x; })
                .attr('cy', function(d) { return d.y; });


            //Exit
            circles.exit().remove();
    }

   

    var myObjects = [
        {x: 100, y: 100},
        {x: 130, y: 120},
        {x: 80, y: 180},
        {x: 180, y: 80},
        {x: 180, y: 40}
    ];
    

    render(myObjects);
<script src="https://cdnjs.cloudflare.com/ajax/libs/d3/4.11.0/d3.min.js"></script>

这是预期的行为,我之前在 中对此进行了解释(不过不是重复的)。

发生的事情是 D3 的创建者 Mike Bostock 在 D3 v2 中引入了一个魔术行为,他在 D3 v3.x 中保留了该行为,但决定在 D3 v4.x 中放弃。要了解更多相关信息,请看这里:What Makes Software Good? 他是这么说的:

D3 2.0 introduced a change: appending to the enter selection would now copy entering elements into the update selection [...] D3 4.0 removes the magic of enter.append. (In fact, D3 4.0 removes the distinction between enter and normal selections entirely: there is now only one class of selection.)

让我们看看。

这是您使用 D3 v3 的代码:

var svg = d3.select('body').append('svg')
  .attr('width', 250)
  .attr('height', 250);

//render the data
function render(data) {
  //Bind 
  var circles = svg.selectAll('circle').data(data);

  //Enter
  circles.enter().append('circle')
    .attr('r', 10);
  //Update
  circles
    .attr('cx', function(d) {
      return d.x;
    })
    .attr('cy', function(d) {
      return d.y;
    });

  //Exit
  circles.exit().remove();
}

var myObjects = [{
  x: 100,
  y: 100
}, {
  x: 130,
  y: 120
}, {
  x: 80,
  y: 180
}, {
  x: 180,
  y: 80
}, {
  x: 180,
  y: 40
}];


render(myObjects);
<script src='https://d3js.org/d3.v3.min.js'></script>

现在使用相同的代码,使用 D3 v4。它将 "break":

var svg = d3.select('body').append('svg')
  .attr('width', 250)
  .attr('height', 250);

//render the data
function render(data) {
  //Bind 
  var circles = svg.selectAll('circle').data(data);

  //Enter
  circles.enter().append('circle')
    .attr('r', 10);
  //Update
  circles
    .attr('cx', function(d) {
      return d.x;
    })
    .attr('cy', function(d) {
      return d.y;
    });

  //Exit
  circles.exit().remove();
}

var myObjects = [{
  x: 100,
  y: 100
}, {
  x: 130,
  y: 120
}, {
  x: 80,
  y: 180
}, {
  x: 180,
  y: 80
}, {
  x: 180,
  y: 40
}];


render(myObjects);
<script src='https://d3js.org/d3.v4.min.js'></script>

"break" 我的意思是圆圈将被追加,但它们不会收到 "enter" 选择中的 xy 属性,它们将默认归零。这就是您在左上角看到所有圆圈的原因。

解决方案:合并选择:

circles.enter().append('circle')
  .attr('r', 10)
  .merge(circles) //from now on, enter + update
  .attr('cx', function(d) {
    return d.x;
  })
  .attr('cy', function(d) {
    return d.y;
  });

根据 APImerge()...

... is commonly used to merge the enter and update selections after a data-join. After modifying the entering and updating elements separately, you can merge the two selections and perform operations on both without duplicate code.

这是带有 merge() 的代码:

var svg = d3.select('body').append('svg')
  .attr('width', 250)
  .attr('height', 250);

//render the data
function render(data) {
  //Bind 
  var circles = svg.selectAll('circle').data(data);

  //Enter
  circles.enter().append('circle')
    .attr('r', 10)
    .merge(circles) //from now on, enter + update
    .attr('cx', function(d) {
      return d.x;
    })
    .attr('cy', function(d) {
      return d.y;
    });

  //Exit
  circles.exit().remove();
}

var myObjects = [{
  x: 100,
  y: 100
}, {
  x: 130,
  y: 120
}, {
  x: 80,
  y: 180
}, {
  x: 180,
  y: 80
}, {
  x: 180,
  y: 40
}];


render(myObjects);
<script src='https://d3js.org/d3.v4.min.js'></script>