d3.js v4 - 节点卡在左上角

时间:2017-07-15 09:23:27

标签: d3.js force-layout

我正在尝试创建一个网页,用户可以在其中选择节点(使用select2),并且他们选择的每个节点都将添加到网络中。 我目前无法使节点出现在SVG上除左上角之外的某个位置。

这是我在bl.ocks.org上的代码: https://bl.ocks.org/shaief/2b4d5cfc7dcc0e03c59f0d6be3cc2913

我希望看到其他人做了什么,但我没有找到解释我的问题的答案。

编辑: 在我看来,虽然代码运行和滴答,但在'ticked'函数中它不会影响节点对象。我无法弄明白为什么。

EDIT2: 我创建了一个新文件,它是Mike Bostock修改力布局II(https://bl.ocks.org/mbostock/0adcc447925ffae87975a3a81628a196)的复制粘贴,并添加了我的select2部分。结果是日志已满:

Uncaught TypeError: Cannot create property 'vx' on string 'node-uuid-1234567890-5'
    at n (d3.v4.min.js:2)
    at d3.v4.min.js:2
    at be.each (d3.v4.min.js:2)
    at e (d3.v4.min.js:2)
    at n (d3.v4.min.js:2)
    at vn (d3.v4.min.js:2)
    at _n (d3.v4.min.js:2)

这很奇怪,因为迈克的例子确实使用节点ID而不是序数索引,我不明白原因。

EDIT3: 我在这里添加相关代码,因为我用完全不同的代码更新了我的要点。

<!DOCTYPE html>
<meta charset="utf-8">

<head>
  <script
      src="https://code.jquery.com/jquery-3.2.1.min.js"
      integrity="sha256-hwg4gsxgFZhOsEEamdOYGBf13FyQuiTwlAQgxVSNgt4="
      crossorigin="anonymous"></script>
  <link href="https://cdnjs.cloudflare.com/ajax/libs/select2/4.0.3/css/select2.min.css" rel="stylesheet" />
  <script src="https://cdnjs.cloudflare.com/ajax/libs/select2/4.0.3/js/select2.min.js"></script>
</head>
<style>
  .container {
    margin-top: 10px;
    display: flex;
  }
  .controls {
    /* padding: 10px; */
    width: 350px;
  }
  #select-node-names {
    width: 100%;
  }
  .d3-container {
    flex-grow: 1;
  }
  .links line {
    stroke: #999;
    stroke-opacity: 0.6;
  }
  .nodes circle {
    stroke: #fff;
    /* stroke-width: 1.5px; */
    stroke-width: 0px;
  }
  text {
    font: 10px sans-serif;
    color: red;
    pointer-events: none;
    text-shadow: 0 1px 0 #fff, 1px 0 0 #fff, 0 -1px 0 #fff, -1px 0 0 #fff;
  }
</style>
<div class='row'>
  <select id="select-node-names" multiple="multiple">
  </select>
  <div class="container">
    <div class="controls">
      <div class="test-nodes"></div>
    </div>
    <div class="d3-container">
        <svg width="960" height="600"></svg>
    </div>
  </div>
  <script src="https://d3js.org/d3.v4.min.js"></script>
  <script>
    d3.json("test.json", function(error, graph) {
      if (error) throw error;
      $("#select-node-names")
        .select2();
      var svg = d3.select("svg"),
        width = +svg.attr("width"),
        height = +svg.attr("height");
      // Color scale for node colors
      var color = d3.scaleOrdinal(d3.schemeCategory10);
      // Color scale for link colors
      var linkColor = d3.scaleOrdinal(d3.schemeCategory10);
      var simulation = d3.forceSimulation(graph.nodes)
        .force("charge", d3.forceManyBody()
          .strength(function(d) {
            return d.degree;
          }))
        .force('x', d3.forceX())
        .force('y', d3.forceY())
        .force("collision", d3.forceCollide(20))
        .force("centering", d3.forceCenter(width / 2, height / 2))
        .force("link", d3.forceLink()
          .id(function(d) {
            return d.id
          }))
        .on("tick", ticked);
      var nodes = [];
      var links = [];
      var nodeNames = Object.values(graph.nodes);
      var listItem = d3.select("#select-node-names")
        .selectAll("option")
        .data(nodeNames)
        .enter()
        .append("option")
        .text(function(d) {
          return d.object_name;
        })
        .attr("id", function(d) {
          return d.id;
        })
        .attr("value", function(d) {
          return d.id;
        });
      linksID = [];
      graph.links.forEach(function(e) {
        var sourceNode = graph.nodes[e.source]["id"];
        var targetNode = graph.nodes[e.target]["id"];
        linksID.push({
          source: sourceNode,
          target: targetNode,
          value: e.type
        });
      });
      $(document.body)
        .on("change", "#select-node-names", function() {
          nodes = search($("#select-node-names")
            .val(), "id", graph.nodes);
          nodesID = nodes.map(function(d) {
            return d.id;
          });
          linksSource = search(nodesID, "source", linksID);
          linksTarget = search(nodesID, "target", linksID);
          links = [];
          links = links.concat(linksSource, linksTarget);
          update();
        });
      var nodesG = svg.append("g");
      var linksG = svg.append("g");
      var node = nodesG
        .selectAll(".nodes")
      var link = linksG
        .selectAll(".links")
      update()
      function update() {
        var showSelectedNodes = d3.select(".test-nodes")
          .html("");
        var showSelectedNodes = d3.select(".test-nodes")
          .selectAll("myText")
          .data(nodes);
        showSelectedNodes.exit()
          .remove(); // EXIT
        showSelectedNodes.enter()
          .append("small") // ENTER
          .text(function(d) {
            return d.object_name + " // ";
          })
          .merge(showSelectedNodes) // ENTER + UPDATE
        var node = nodesG
          .selectAll("circle")
          .data(nodes);
        exitNode = node.exit()
          .remove(); // EXIT
        node = node.enter()
          .append("circle") // ENTER
          .attr("class", "nodes")
          .attr("r", 10)
          .attr("fill", function(d) {
            return color(d.node_type);
          })
          .append("title")
          .text(function(d) {
            return "Node: " + d.id + "\nName: " + d.object_name +
              "\nCoordinates: " + d.x + " - " + d.y;
          })
          .append("text")
          .attr("dx", 8)
          .attr("dy", ".31em")
          .text(function(d) {
            return d.object_name;
          })
          .call(d3.drag()
            .on("start", dragstarted)
            .on("drag", dragged)
            .on("end", dragended))
          .merge(node) // ENTER + UPDATE
        link = link.enter()
          .data(links)
          .append("line")
          .style('stroke', function(d) {
            return linkColor(d.type);
          })
          .attr("stroke-width", 2)
          .append("title")
          .text(function(d) {
            return "Source: " + d.source + "\nTarget: " + d.target +
              "\nType: " + d.type;
          });
        // Update and restart the simulation.
        simulation.nodes(nodes);
        simulation.force("link", d3.forceLink()
          .id(function(d) {
            return d.id
          }));
        simulation.alphaTarget(1);
        simulation.restart();
      }
      function ticked() {
        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("cx", function(d) {
            return d.x;
          })
          .attr("cy", function(d) {
            return d.y;
          })
          /* node.attr("transform", transform);*/
      }
      function dragstarted(d) {
        if (!d3.event.active) simulation.alphaTarget(0.3)
          .restart();
        d.fx = null;
        d.fy = null;
      }
      function dragged(d) {
        d.fx = d3.event.x;
        d.fy = d3.event.y;
      }
      function dragended(d) {
        if (!d3.event.active) simulation.alphaTarget(0);
        d.fx = null;
        d.fy = null;
      }
      function transform(d) {
        return "translate(" + d.x + "," + d.y + ")";
      };
    });
    function search(valuesArray, prop, myArray) {
      var res = [];
      for (var i = 0; i < valuesArray.length; i++) {
        for (var j = 0; j < myArray.length; j++) {
          if (myArray[j][prop] == valuesArray[i]) {
            res.push(myArray[j]);
            break;
          }
        }
      }
      return res
    }
  </script>

1 个答案:

答案 0 :(得分:0)

我看到的直接事情:

  1. 您并未在所有情况下都将链接添加到模拟中:应通过simulation.force(&#34; ..&#34;)。链接( link-array)完成

  2. 您需要明确您用于链接的ID。你需要使用节点的顺序位置,例如0是第一个节点。或节点的实际id,例如&#34; node-uuid-1234567890-1&#34;。你正在为后者而努力。阅读enter image description here以选择您要使用的是哪一个。在你的json中,你使用数字作为链接源和目标。

  3. 总结这些要点,这可能是您基于已完成的重启逻辑,但您需要与您的ID使用保持一致:

      simulation.nodes(nodes);
      simulation.force("link").links(links);
      simulation.alpha(1).restart();
    

    了解Mike的links.id api ref,了解如何更新模拟。你做了太多工作