d3.js - v3和v4版本 - Enter和Update的区别

5
我正在尝试使用d3.js v4获取xy的值来创建圆。使用以下代码,我成功地创建了圆形的图表,但是当我尝试在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>

2个回答

10

这是预期的行为,并且我以前在这个答案中解释过(虽然不是重复的)。

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

D3 2.0引入了一个改变:附加到进入选择现在会将进入元素复制到更新选择中[...] D3 4.0删除了enter.append的魔力。(事实上,D3 4.0完全删除了enter和普通选择之间的区别:现在只有一类选择。)

让我们来看看它。

这是使用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 的相同代码,它会“破坏”:

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”选择中接收到“x”和“y”属性,并且它们默认为零。这就是为什么您会看到所有的圆圈都在左上角。

解决方案:合并选择:

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()通常用于在数据绑定后合并enter和update选择集。在单独修改进入和更新元素后,可以合并这两个选择集,并对两者执行操作而无需重复代码。

这是包含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>


谢谢您的回答,我现在明白了v3和v4之间的区别。 - Richard

2

d3v4中更新模式已更改。以下是文档的摘录:

此外,selection.append不再将进入节点合并到更新选择中;在数据连接后使用selection.merge来组合enter和update。

您应该按照以下方式重写代码:

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>


1
谢谢你的回答,它帮了我很多! - Richard

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接