d3.js中的强制图 - 消失的节点

时间:2013-10-10 08:56:26

标签: d3.js force-layout

我正在尝试在d3.js中构建一个定向力图,它上面有一个点击监听器,用于更改基础数据并重绘图形。我相信我遵循Bostock先生的更新模式,但我遇到的问题是当我运行由点击监听器触发的更新时,节点从屏幕底部消失,留下链接和标签。

此更新似乎运行,更新现有节点(在这种情况下将它们变为绿色)然后忽略“enter”和“exit”部分(这是所需的行为)然后点击发送节点的tick()函数南。

我可以通过删除节点上的“g”标签并因此解耦标签和节点来实现这一点,这显然是不可取的。

我情不自禁地觉得我错过了一些明显的东西!或许我应该以不同的方式解决这个问题?

以下是代码:

var width = 960,
    height = 500,
    links,
    nodes,
    root;

var force = d3.layout.force()
    .size([width, height])
    .charge(-200)
    .linkDistance(50)
    .on("tick", tick);

var svg = d3.select("body").append("svg")
    .attr("width", width)
    .attr("height", height);

var link = svg.selectAll(".link"),
    node = svg.selectAll(".node");

d3.json("test.json", function(json) {
  root = json;
  update();
});

function update() {
  nodes = root.nodes
  links = root.links

  // Restart the force layout.
  force
      .nodes(nodes)
      .links(links)
      .start();

    svg.append("svg:defs").append("marker")
        .attr("id", "end")
        .attr("refX", 15)
        .attr("refY", 2)
        .attr("markerWidth", 6)
        .attr("markerHeight", 4)
        .attr("orient", "auto")
        .append("svg:path")
        .attr("d", "M 0,0 V 4 L8,2 Z");

  // Update the links…
  //link = link.data(links, function(d) { return d.target.name; });
  link = link.data(links)

  // Exit any old links.
  link.exit().remove();

  // Enter any new links.
  link.enter().insert("svg:line", ".node")
      .attr("class", "link")
      .attr("marker-end", "url(#end)")
      .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; });

  // Update the nodes…
  node = svg.selectAll("g").select(".node").data(nodes, function(d) { return d.name; });


  node.style("fill", "green")

  // Exit any old nodes.
  node.exit().remove();

  // Enter any new nodes.
  node.enter().append("g")
      .append("svg:circle")
      .attr("class", "node")
      .attr("id", function(d) {return "node" + d.index; })
      .attr("r", 12)
      .style("fill", "#BBB")
      .on("click", click)
      .call(force.drag);

  node.append("svg:text")
      .attr("dx", 16)
      .attr("dy", ".15em")
      .attr("class", "nodelabel")
      .text(function(d) { return d.name });
}

function tick() {
  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; });

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

}


function click(d) {
  if (!d3.event.defaultPrevented) {

    // DO ANYTHING
    update()
  }
}

,test.json的内容是:

{
  "nodes": [
    {"name" : "John"},
    {"name" : "Alison"},
    {"name" : "Phil"},
    {"name" : "Jim"},
    {"name" : "Jane"},
    {"name" : "Mary"},
    {"name" : "Joe"}
  ],
  "links": [
    {"source":  1, "target":  0},
    {"source":  2, "target":  0},
    {"source":  3, "target":  0},
    {"source":  4, "target":  0},
    {"source":  5, "target":  1},
    {"source":  6, "target":  1}
  ]
}

1 个答案:

答案 0 :(得分:1)

好的,所以我发现了问题。当我选择要更新的节点时,我正在选择节点(即具有“节点”类的元素)并且它们正在更新:

node = svg.selectAll("g").select(".node").data(nodes, function(d) { return d.name; });

然后在tick函数中我正在更新这些节点:

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

所以这是非常正确的,然而,节点被封装在“g”标签中以及文本标签,但tick()功能仅作用于节点。修复是强制tick()中的transform属性更新整个组而不仅仅是节点:

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

现在一切正常!