D3 v4:更新力布局 [英] D3 v4: Update force layout

查看:3312
本文介绍了D3 v4:更新力布局的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

Mike Bostock 有一个关于更新强制布局的示例。该示例基于v3 - 如何在v4中复制相同的功能?

Mike Bostock has an example regarding updating a force layout. The example is based on v3 — how can the same functionality be replicated in v4?

这是我(可怜的)尝试

我读过更改到v4 Changelog中的选择,但合并调用仍然令人困惑。特别是,我不清楚数据连接如何与模拟 nodes() links()

I've read the changes to selections in the v4 Changelog, but the merge call is still confusing. In particular, it's not clear to me how the data join interacts with the simulation nodes() and links() call.

推荐答案

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>

因此,你实际上不需要d3选择 - 合并,使你的示例工作。原因是您的节点位置和链接正在通过模拟进行更新。因此,您将需要在开始时添加节点和链接,但是任何对位置的更新都会在模拟开始时在start方法结束时发生。

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.

一个你的原始代码的主要缺陷是你调用svg.selectAll('。node')& svg.selectAll('。link')在脚本的初始阶段。当你这样做,没有任何节点或链接绑定到svg,所以你得到一个d3选择与空DOM元素。这是很好,当你想添加元素与enter()。append() - 但是,当删除元素时,这将不工作。使用同样的,过时的d3选择删除元素与exit()。remove()不工作b / c没有任何DOM元素d3选择要删除。你需要每次调用svg.selectAll()获取当前在svg中的DOM元素。

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.

我也在你的代码做了一些小的改动。您通常希望链接始终显示在节点下方。当您向SVG添加元素时,最近添加的节点将放置在顶部。但是,如果您之前添加组(如我在代码中使用linkLayer& nodeLayer),则任何新添加的链接将显示在nodeLayer组中的任何项目下方。

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.

这篇关于D3 v4:更新力布局的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆