【问题标题】:d3.js v4 - nodes are stuck in the top left cornerd3.js v4 - 节点卡在左上角
【发布时间】:2017-12-20 08:37:48
【问题描述】:

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

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

我想看看其他人做了什么,但我找不到能解释我的问题的答案。

编辑: 在我看来,尽管代码运行并滴答作响,但在“勾选”函数中它不会影响节点对象。但我不知道为什么。

编辑2: 我创建了一个新文件,它是 Mike Bostock 的 Modifying a Force Layout 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)

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

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

<!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>

【问题讨论】:

    标签: d3.js force-layout


    【解决方案1】:

    我立即看到的东西:

    1. 您不会在所有情况下都将链接添加到模拟:应该通过 Simulation.force("..").links(link-array)

    2. 您需要清楚您用于链接的 id。您需要使用节点的序数位置,例如0 是第一个节点。或节点的实际 ID,例如“node-uuid-1234567890-1”。你正在尝试后者。阅读links.id api ref 以选择您要使用的那个。在您的 json 中,您使用数字作为链接源和目标。

    总结这些点,这应该是基于你所做的重启逻辑,但你需要与你的 id 使用保持一致:

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

    查看 Mike 的 block for updating a force layout 以清楚了解如何更新模拟。你做的太多了

    【讨论】:

    • 谢谢!澄清一下,这是一个模拟的 JSON,但它有源和目标的数字,因为它通过 Python 的 NetworkX。
    • 好的。所以在我看来,我在我的代码中遵循了 links.id API——我通过使用它们的 ID 而不是它们的索引来引用节点的每个链接。当我将其更改为默认行为时,我得到了这个:Uncaught TypeError: Cannot create property 'vx' on string 'node-uuid-1234567890-2'
    • 如果使用字符串id,需要像这样指定链接力: var simulation = d3.forceSimulation() .force("link", d3.forceLink().id(function( d) { return d.id; })) 这样你的 json 链接是: {source: "node-uuid-1234567890-1" target: "node-uuid-1234567890-2" value: 1} 。 vx 是速度 x 位置,它基本上是说,我无法弄清楚创建这个属性来移动你的节点/链接
    • 谢谢,但正如您在我的代码中看到的那样,我已经这样做了,但它在刻度部分没有发挥它的魔力。知道为什么吗?
    • 如果你不同时添加“源”节点和“目标”节点,它总是会失败,因为它无法绘制链接。那是最大的问题。你的合并看起来也不正确,你的输入/合并/删除没有完成
    猜你喜欢
    • 2018-06-21
    • 2023-03-21
    • 2017-11-12
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2018-03-26
    • 2017-04-19
    • 1970-01-01
    相关资源
    最近更新 更多