D3 v4群组更新模式

时间:2017-01-13 01:24:07

标签: javascript d3.js

给出以下布局:

<g>
   ... // many nodes
   <g>
      <circle></circle>
      <text></text>
   </g>
   ...
</g>

d3 v4中的正确更新模式如何? 我必须在merge(?)中使用什么作为参数,我多久需要调用一次合并(仅在节点上?节点+圆+文本?)

我在小提琴上创建了一个工作示例:https://jsfiddle.net/cvvfsg97/6/

代码:

function update(items) {
  node = nodeLayer.selectAll(".node")
    .data(items, function(d) { return d.id; })

  node = node.enter() // insert
    .append("g")
    .attr("class", "node");

  node.append("circle") // insert
    .attr("r", 2.5)
    .attr('class', 'circle')
    .merge(nodeLayer.selectAll('.node > circle')) // is this correct?! // merge
    .attr('fill', 'red') // just for testing purposes
    .exit().remove(); // exit

  node.append("text") // insert
      .attr("dy", 3)
      .text(function(d) { return d.name; })
      .merge(nodeLayer.selectAll('.node > text')) // is this correct?! // merge
      .attr('fill', 'green')  // just for testing purposes
      .exit().remove();

    node.merge(nodeLayer.selectAll('.node')) // is this correct?!  // merge 
    .attr('class', 'anotherClass')
    .exit().remove(); // does not work // exit
}

有人可以在如何使用组中的enter(),merge(),exit()方面明确一些吗?

我可能希望对每个元素的每个阶段进行更改。

更新:我简化了示例,我不需要链接或强制布局。我的问题只是关于更新模式,而不是关于力量。更新后的jsfiddle没有强制布局。

3 个答案:

答案 0 :(得分:4)

你的模式过于复杂。这是您正确编写的更新函数:

function update(items) {

  var node = nodeLayer.selectAll(".node") // bind the data, this is update
    .data(items, function(d) {
      return d.id;
    });

  node.exit().remove(); // exit, remove the g

  nodeEnter = node.enter() // enter, append the g
    .append("g")
    .attr("class", "node");

  nodeEnter.append("circle") // enter, append the circle on the g
    .attr("r", 2.5)
    .attr('class', 'circle')
    .attr('fill', 'red');

  nodeEnter.append("text") // enter, append the text on the g
    .attr("dy", 3)
    .text(function(d) {
      return d.name;
    })
    .attr('fill', 'green');

  node = nodeEnter.merge(node); // enter + update on the g

  node.attr('transform', function(d){ // enter + update, position the g
    return 'translate(' + d.x + ',' + d.y + ')';
  });

  node.select("text") // enter + update on subselection
    .text(function(d) {
      return d.name;
    });

}

此处它正在运行多个调用:

<!DOCTYPE html>
<html>

<head>
  <script data-require="d3@4.0.0" data-semver="4.0.0" src="https://d3js.org/d3.v4.min.js"></script>
</head>

<body>
  <script>
  
    var nodeLayer = d3.select('body')
      .append('svg')
      .attr('width',500)
      .attr('height',500);
      
    update([
      {
        id: 1,
        name: 'A',
        x: Math.random() * 500,
        y: Math.random() * 500
      },{
        id: 2,
        name: 'B',
        x: Math.random() * 500,
        y: Math.random() * 500
      },{
        id: 3,
        name: 'C',
        x: Math.random() * 500,
        y: Math.random() * 500
      }
    ]);
    
    setTimeout(function(){
        update([
        {
          id: 1,
          name: 'A',
          x: Math.random() * 500,
          y: Math.random() * 500
        },{
          id: 4,
          name: 'This is a new name...',
          x: Math.random() * 500,
          y: Math.random() * 500
        },{
          id: 3,
          name: 'C',
          x: Math.random() * 500,
          y: Math.random() * 500
        }
      ]);
    }, 3000);
  
    function update(items) {
      
      var node = nodeLayer.selectAll(".node")
        .data(items, function(d) {
          return d.id;
        });
        
      node.exit().remove(); // exit, remove the g

      nodeEnter = node.enter() // enter the g
        .append("g")
        .attr("class", "node");
        
      nodeEnter.append("circle") // enter the circle on the g
        .attr("r", 2.5)
        .attr('class', 'circle')
        .attr('fill', 'red');

      nodeEnter.append("text") // enter the text on the g
        .attr("dy", 3)
        .attr('fill', 'green');
        
      node = nodeEnter.merge(node); // enter + update
          
      node.attr('transform', function(d){
        return 'translate(' + d.x + ',' + d.y + ')';
      });
      
      node.select("text")
       .text(function(d) {
         return d.name;
       });

    }
  </script>
</body>

</html>

答案 1 :(得分:-1)

我最近在我的代码中完成了这个 - 我在包含元素的当前选择上使用select(subSelector)。在您的示例中,我将更改如下:

function update(items) {
var node = nodeLayer.selectAll(".node")
    .data(items, function(d) { return d.id; })

var nodeEnter = node.enter()
    .append("g")
    .attr("class", "node");

nodeEnter.append("circle")
    .attr("r", 2.5)
    .attr('class', 'circle')
    .merge(node.select('circle'))
    .attr('fill', 'red');

nodeEnter.append("text") // insert
    .attr("dy", 3)
    .text(function(d) { return d.name; })
    .merge(node.select('text'))
    .attr('fill', 'green');

// You only need to call remove on the group, all the other exit().remove() calls are superfluous
node.exit().remove();

simulation
    .nodes(items);

}

答案 2 :(得分:-1)

var svg = d3.select("svg"),
  width = +svg.attr("width"),
  height = +svg.attr("height"),
  nodeLayer = svg.append('g'),
  node;

var list = [];
var links = [];

var simulation = d3.forceSimulation(list)
  .force("charge", d3.forceManyBody().strength(-1000))
  .force("link", d3.forceLink(links).distance(200))
  .force("center", d3.forceCenter(width / 2, height / 2))
  .on("tick", ticked);

function createNodes(index) {
  links.push({
    'source': 0,
    'target': index
  })
  list.push({
    "id": index,
    "name": "server " + index
  });
  return list;
}

var iteration = 0;
update(createNodes(iteration)); // just simulating updates 

d3.interval(function(timer) {
  iteration++;
  update(createNodes(iteration));
}, 1000); //<-- this was commented out incorrectly just now

function update(items) {
  var dataJoin = nodeLayer.selectAll(".node")
    .data(items, function(d) {
      return d.id;
    });
  node = dataJoin.enter() // insert
    .append("g")
    .attr("class", "node");

  node.append("circle") // insert
    .attr("r", 2.5)
    .attr('class', 'circle')
    .merge(dataJoin) // not the class, the actual selected group you called enter() on
    .select('.circle')
    .style('fill', 'red') // just for testing purposes
    .exit().remove(); // exit

  node.append("text") // insert
    .attr("dy", 3)
    .attr('class', 'text')
    .text(function(d) {
      return d.name;
    })
    .merge(dataJoin)
    .select('.text')
    .style('fill', 'green') // fill is a style

  dataJoin.exit().remove();
  simulation.nodes(list);
  simulation.force("link").links(links);
  simulation.alpha(1).restart();
}

function ticked() {
  node.attr("transform", function(d) {
    var a = d.x || 0;
    var b = d.y || 0;
    return "translate(" + a + ", " + b + ")";
  });
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/d3/4.4.1/d3.min.js"></script>
<svg height="300" width="300"></svg>

有一堆错误,

首先应该使用所有数据节点的列表调用'更新'(misnomer),而不仅仅是更改。

你可能想要探索https://bl.ocks.org/并复制人们如何使用力导向图。你需要有力量的链接。

合并背后的想法是将新列表与旧列表进行比较,这意味着您需要使用dataJoin或附加了数据/ ID的组。

我不是专家,请查看力导向图的所有示例,并了解它们如何更新/合并。 (有多种方法可以更新/重新启动图表)