D3 v4: Update force layout

本小妞迷上赌 提交于 2019-11-30 09:15:47

var width = 300,
    height = 200;

var color = d3.scaleOrdinal(d3.schemeCategory20);

var nodes = [],
    links = [];

var simulation = d3.forceSimulation()
    .force("link", d3.forceLink().id(function(d) { return d.id; }))
    .force("charge", d3.forceManyBody())
    .force("center", d3.forceCenter(width / 2, height / 2));

var svg = d3.select("svg");

var linkLayer = svg.append('g').attr('id','link-layer');
var nodeLayer = svg.append('g').attr('id','node-layer');

// 1. Add three nodes and three links.
setTimeout(function() {
  var a = {id: "a"}, b = {id: "b"}, c = {id: "c"};
  nodes.push(a, b, c);
  links.push({source: a, target: b}, {source: a, target: c}, {source: b, target: c});
  start();
}, 0);

// 2. Remove node B and associated links.
setTimeout(function() {
  nodes.splice(1, 1); // remove b
  links.shift(); // remove a-b
  links.pop(); // remove b-c
  start();
}, 2000);

// Add node B back.
setTimeout(function() {
  var a = nodes[0], b = {id: "b"}, c = nodes[1];
  nodes.push(b);
  links.push({source: a, target: b}, {source: b, target: c});
  start();
}, 4000);


function start() {
  var link = linkLayer.selectAll(".link")
    .data(links, function(d) { return d.source.id + "-" + d.target.id; });
  
  
  link.enter().append("line")
    .attr("class", "link");

  link.exit().remove();

  var node = nodeLayer.selectAll(".node")
      .data(nodes, function(d) { return d.id;});
  
  node.enter().append("circle")
      .attr("class", function(d) { return "node " + d.id; })
      .attr("r", 8);

  node.exit().remove();

  simulation
    .nodes(nodes)
    .on("tick", tick);

  simulation.force("link")
    .links(links);
}

function tick() {
  nodeLayer.selectAll('.node').attr("cx", function(d) { return d.x; })
      .attr("cy", function(d) { return d.y; })

  linkLayer.selectAll('.link').attr("x1", function(d) { return d.source.x; })
      .attr("y1", function(d) { return d.source.y; })
      .attr("x2", function(d) { return d.target.x; })
      .attr("y2", function(d) { return d.target.y; });
}
.link {
  stroke: #000;
  stroke-width: 1.5px;
}

.node {
  fill: #000;
  stroke: #fff;
  stroke-width: 1.5px;
}

.node.a { fill: #1f77b4; }
.node.b { fill: #ff7f0e; }
.node.c { fill: #2ca02c; }
<script src="https://d3js.org/d3.v4.min.js"></script>
<svg width="300px" height="200px"></svg>

So you don't actually need a d3-selection-merge to make your example work. The reason is that your node positions and links are being updated by the simulation. So you will want to add the nodes and links on start, but any updating to the positions will happen at the end of the start method when the simulation is started.

One major flaw with your original code was that you called svg.selectAll('.node') & svg.selectAll('.link') at the initial stages of the script. When you do this, there aren't any nodes or links bound to the svg, so you get a d3-selection with empty DOM elements. This is fine when you want to add elements with enter().append() - however when removing elements this will not work. Using that same, stale d3-selection to remove elements with exit().remove() does not work b/c there aren't any DOM elements in the d3-selection to remove. You need to call svg.selectAll() each time to get the DOM elements that are currently in the svg.

I also made a few minor changes in your code. You usually want the links to always show below the nodes. As you're adding elements to an SVG, the most recently added nodes are placed at the top. However if you add groups before hand (as I did with linkLayer & nodeLayer in the code), any newly added links will appear below any items in the nodeLayer group.

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!