D3v4插入新节点

时间:2018-08-10 13:02:34

标签: javascript d3.js

编辑:added codepen example

我正在尝试D3v4力仿真,但目前无法正确地向图中添加新节点。这是设置

var svgRef = d3.select("body").append("svg")
    .attr("width",svgWidth)
    .attr("height",svgHeight)
    .on("mousedown",svgClick);

var nodes_data = [{id: "0"},{id: "1"},{id: "2"}];
var links_data = [{source:"1",target:"2"}];

var linksRef = svgRef
    .selectAll(".link")         
    .data(links_data).enter()
    .append("line").attr("class", "link");

var nodesRef = svgRef
    .append("g").attr("class", "nodes")
    .selectAll(".node")
    .data(nodes_data).enter()
    .append("g").attr("class", "node")
    .call(d3.drag()...);

nodesRef.append("circle")
    .attr("r", nodeRadius);

我正在为单个节点创建一个组,以容纳它们的circletitletext等,所有这些节点都在一个更大的“节点”组下创建。最终结果如下:

<svg>
    ...
    <g class="nodes">
        <g class="node">
            <circle>
            <text>
            ...
        </g>
        <g class="node"></g>
        <g class="node"></g>
        ...
    </g>
</svg>

然后我要在数据中添加一个新节点,然后尝试将其插入图形中

function svgClick(){

    // add new node
    nodes_data.push({id: nodeCount++, "x":d3.event.X, "y":d3.event.y});

    // restart simulation
    simulation.nodes(nodes_data).on("tick", ticked);

    // insert new nodes
    nodeEnter = svgRef
        .selectAll(".node")
        .data(nodes_data).enter()
        .append("g").attr("class", "node")
        .call(d3.drag()...)
        .append("circle")
            .attr("class", "node")
            .attr("r", nodeRadius);

    // merge new nodes
    nodesRef = nodeEnter.merge(nodesRef);
}

我无法修复的2处出问题的地方:

  • 插入的节点未出现在鼠标坐标上 弄清楚了这个,只是d3.event.X中的一个错字,其中 X 必须小写

  • 未将插入的节点添加到<g class="nodes">中,但添加到下面。我找不到正确选择它的方法

像这样:

<svg>
    <g class="nodes">
        <g class="node"></g>
        <g class="node"></g>
    </g>
    <g class="node"></g>  // <--- inserted here
</svg>

1 个答案:

答案 0 :(得分:1)

对于第二个问题:“插入的节点未添加到,而是在下面。我找不到正确选择它的方法”,如何输入新节点说明了为什么最终拥有自己的结构:

nodeEnter = svgRef
 .selectAll(".node")   // select all elements with class node in the svg
 .data(nodes_data)     // bind new data to them
  .enter()             // enter new nodes where needed
 .append("g")          // append a g for each new node needed to the svg

这会将新节点直接附加到父svg:svgRef.selectAll(...)...

如果要将新节点附加到父级,则必须首先选择该父级。如果我们想将g与类nodes一起用作新节点的父元素,我们将使用:svgRef.select(".nodes").selectAll(...)...

这看起来像:

nodeEnter = svgRef
  .select(".nodes")     // select the parent g with class nodes
  .seleactAll(".node")  // select all nodes in that parent
  .data(nodes_data)     // bind new data to them
  .enter()              // enter new nodes where needed
  .append("g")          // append a g for each new node needed to the g with class nodes

此外,您还希望nodeEnter是g元素,而不是circle元素,因此您可以在此处拆分链接。圆圈也不需要类节点,因为父级g具有它。

对于定位,您使用的是d3.event.X而不是d3.event.x,并且在codepen中,相对于问题,您使用的是节点的cx,cy属性(力将位置设为是x,y属性)。

这里是updated pen

请记住,如果tick结束,则新节点的位置不会像放置在每个tick上一样,而不是放在新节点上(如果要确保tick,请重置alpha)。同样,您可能会看到的跳动是由于d3.forceCenter试图确保节点的重心是指定点(如果要避免该问题,可以施加定位力而不是d3.forceCenter)