使用D3 v4重新创建可折叠力导向图

3
我一直在尝试使用d3js v4创建可折叠强制布局,类似于此链接中的布局:https://mbostock.github.io/d3/talk/20111116/force-collapsible.html。我已经成功创建了布局本身,但是无法更新它。有人能提供帮助吗?以下是我的js代码:

var width = 960,
    height = 600;

var root = {
 "name": "server1900",
 "children": [{
  "name": "server913",
  "_children": null,
  "children": [{
   "name": "server948"
  }, {
   "name": "server946"
  }]
 }, {
  "name": "server912",
  "_children": null,
  "children": [{
   "name": "server984"
  }, {
   "name": "server983"
  }]
 }, {
  "name": "server911",
  "_children": null,
  "children": [{
   "name": "server999",
   "_children": null,
   "children": [{
    "name": "server992"
   }]
  }]
 }]
};

root = d3.hierarchy(root);

var i = 0;

var transform = d3.zoomIdentity;;

var nodeSvg, linkSvg, simulation, nodeEnter, linkEnter ;

var svg = d3.select("body").append("svg")
    .attr("width", width)
    .attr("height", height)
    .call(d3.zoom().scaleExtent([1 / 2, 8]).on("zoom", zoomed))
  .append("g")
    .attr("transform", "translate(40,0)");

function zoomed() {
  svg.attr("transform", d3.event.transform);
}

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))
  .on("tick", ticked);

update();

function update() {
  var nodes = flatten(root);
  var links = root.links();

  simulation
    .nodes(nodes)

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

  linkSvg = svg.selectAll(".link")
    .data(links, function(d) { return d.target.id; })

  linkSvg.exit().remove();

  linkSvg = linkSvg.enter()
      .append("line")
      .attr("class", "link");

  nodeSvg = svg.selectAll(".node")
    .data(nodes, function(d) { return d.id; })

  nodeSvg.exit().remove();

  nodeSvg = nodeSvg.enter()
    .append("g")
      .attr("class", "node")
      .on("click", click)
      .call(d3.drag()
        .on("start", dragstarted)
        .on("drag", dragged)
        .on("end", dragended))

    nodeSvg.append("circle")
      .attr("r", 4  )
      .append("title")
        .text(function(d) { return d.data.name; })

    nodeSvg.append("text")
      .attr("dy", 3)
      .attr("x", function(d) { return d.children ? -8 : 8; })
      .style("text-anchor", function(d) { return d.children ? "end" : "start"; })
      .text(function(d) { return d.data.name; });



}

function ticked() {
  linkSvg
      .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; });

  nodeSvg
      .attr("transform", function(d) { return "translate(" + d.x + ", " + d.y + ")"; });
}

function click(d) {
 if (d.children) {
  d._children = d.children;
  d.children = null;
    update();
    simulation.restart();
 } else {
  d.children = d._children;
  d._children = null;
    update();
    simulation.restart();
 }
}

function dragstarted(d) {
  if (!d3.event.active) simulation.alphaTarget(0.3).restart()
  simulation.fix(d);
}

function dragged(d) {
  simulation.fix(d, d3.event.x, d3.event.y);
}

function dragended(d) {
  if (!d3.event.active) simulation.alphaTarget(0);
  simulation.unfix(d);
}

function flatten (root) {
  // hierarchical data to flat data for force layout
  var nodes = [];
  function recurse(node) {
    if (node.children) node.children.forEach(recurse);
    if (!node.id) node.id = ++i;
    else ++i;
    nodes.push(node);
  }
  recurse(root);
  return nodes;
}
line {
  stroke: #666;
}

.node {
  pointer-events: all;
}

circle {
  stroke: none;
  stroke-width: 40px;
}

.node text {
  font: 8px sans-serif;
}
<script src="https://d3js.org/d3.v4.0.0-alpha.50.min.js"></script>

这是我的代码示例。 https://jsfiddle.net/t4vzg650/4/ 谢谢。

你指的是什么样的更新?有任何错误吗,还是只是没有显示所期望的行为? - altocumulus
点击节点后,如果该节点有子节点,则子节点将展开/折叠,即地图将更新以显示/隐藏新节点。 我正在指的是已更新的节点。没有错误。未显示所期望的行为。 - kirupakaranh
你确定这个d3.version.alpha没有错误吗? - Klaujesi
@Klaujesi 我不确定。但是,d3 v4即将发布。希望它足够稳定。而且,我用d3 v3做的大数据集图表存在性能问题。因此,尝试使用v4来完成,希望性能会有所提高。问题是,d3 v4有一些重大变化。我觉得我可能错过了什么。 - kirupakaranh
我忘记调用simulation.restart()了。现在添加和删除节点似乎可以工作,但节点和链接位置不正确。已更新fiddle。 - kirupakaranh
我可能没有正确地添加和删除子元素? - kirupakaranh
1个回答

7

我忘记在enter()后合并旧节点。

link = svg.selectAll(".link").data(links, function(d) { return d.target.id; })
var linkEnter = link.enter().append("line").attr("class", "link");
link = linkEnter.merge(link);

感谢 Mike Bostock 帮我解决了这个问题。我以为是 d3 v4 的问题,结果发现自己没有完全阅读变更记录 :|
更多信息请参考:https://github.com/d3/d3-force/issues/37 修复后的示例:https://jsfiddle.net/t4vzg650/6/

1
非常有帮助。对于其他正在浏览此内容的人,确保使用@kirupakaranh(正确)使用的关键函数(https://bost.ocks.org/mike/constancy/#key-functions)也可能很有用:`function(d) { return d.id; }`。有很多移动部件,除了合并之外,关键函数还解决了我的问题。 - user4275029

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