【问题标题】:D3 Linking nodes based on names rather than indexD3 基于名称而不是索引链接节点
【发布时间】:2018-07-17 20:44:20
【问题描述】:

我正在尝试根据 Id 而不是 codepen 中的索引链接节点,我不知道该怎么做。

以下作品:

var links = [{ source:  0, target: 1 }];

但如果我根据名称创建链接,它不会创建任何链接。

var links = [{ source: "FH" , target: "TP"  }];

【问题讨论】:

    标签: javascript d3.js


    【解决方案1】:

    D3v3 强制布局不支持链接中的命名节点。如果您想在不需要任何额外工作的情况下使用它们,我建议您使用 d3v4 或 v5。

    但是,与REEE 的回答一样,您可以通过更改代码并保留 v3.0 来达到相同的效果。我没有更改刻度,而是在启动数组之前更改 link 数组:

    var obj = {}
    nodes.forEach(function(d,i){
      obj[d.id] = i;             // create an object to look up a node's index by id
    })
    
    links.forEach(function(d) {
      d.source = obj[d.source];  // look up the index of source
      d.target = obj[d.target];  // look up the index of target
    })
    

    这是一个分叉的plunkr,或下面的一个 sn-p(我已将您的节点数组移动到它自己的变量中 - 是的,它被称为节点,稍后会被覆盖):

    var width = 500,
      height = 200;
    
    var fill = d3.scale.category20();
    var links = [{ source:  "FH", target: "TP" }];
    var nodes = [
        { id: "FH", x: 100, y: 110 },
        { id: "TP", x: 200, y: 110 },
        { id: "GW", x: 200, y: 110 },
        { id: "DB", x: 100, y: 110 }
      ]
    
    var obj = {}
    nodes.forEach(function(d,i){
      obj[d.id] = i;
    })
    
    links.forEach(function(d) {
      d.source = obj[d.source];
      d.target = obj[d.target];
    })
    
    var force = d3.layout
      .force()
      .size([width, height])
      .nodes(nodes)
      .links(links)
      .linkDistance(150)
      .charge(-500)
      .on("tick", tick);
    
    var svg = d3
      .select("body")
      .append("svg")
      .attr("width", width)
      .attr("height", height);
    
    var arrows = svg
      .append("svg:defs")
      .selectAll("marker")
      .data(["arrow"])
      .enter()
      .append("marker")
      .attr("id", String)
      .attr("viewBox", "0 -5 10 10")
      .attr("refX", 15)
      .attr("refY", -1.5)
      .attr("markerWidth", 6)
      .attr("markerHeight", 6)
      .attr("orient", "auto")
      .append("path")
      .attr("d", "M0,-5L10,0L0,5");
    
    svg
      .append("rect")
      .attr("width", width)
      .attr("height", height);
    
    var nodes = force.nodes(),
      links = force.links(),
      node = svg.selectAll(".node"),
      link = svg.selectAll(".link");
    
    restart();
    
    
    
    
    
    function tick() {
      link.attr("d", function(d) {
        var dx = d.target.x - d.source.x,
          dy = d.target.y - d.source.y,
          dr = Math.sqrt(dx * dx + dy * dy);
        return (
          "M" +
          d.source.x +
          "," +
          d.source.y +
          "A" +
          dr +
          "," +
          dr +
          " 0 0,1 " +
          d.target.x +
          "," +
          d.target.y
        );
      });
    
      node.attr("transform", function(d) {
        return "translate(" + d.x + "," + d.y + ")";
      });
    }
    
    function restart() {
      node = node.data(nodes);
      node
        .enter()
        .insert("g")
        .attr("class", "node")
        .call(force.drag);
      node
        .append("image")
        .attr("xlink:href", "https://github.com/favicon.ico")
        .attr("x", -8)
        .attr("y", -8)
        .attr("width", 16)
        .attr("height", 16);
      node
        .append("text")
        .attr("dx", 12)
        .attr("dy", ".35em")
        .text(function(d) {
          return d.id;
        });
      node.exit().remove();
    
      link = link.data(links);
      link
        .enter()
        .append("path")
        .attr("class", "link")
        .attr("marker-end", "url(#arrow)");
      link.exit().remove();
    
      force.start();
    }
    #nodeConsole {
      width: 80%;
      height: 1px;
      font-family: courier new;
      padding: 1px;
      border: 3px solid gray;
      margin-top: 1px;
      overflow: autao;
    }
    
    #linkedNodes {
      width: 80%;
      font-family: courier new;
      padding: 10px;
    }
    
    #srcNodes {
      width: 40%;
      font-family: courier new;
      padding: 8px;
    }
    
    #targetNodes {
      width: 40%;
      font-family: courier new;
      padding: 8px;
    }
    
    rect {
      fill: none;
      pointer-events: all;
    }
    
    .node {
      fill: #000;
    }
    
    .cursor {
      fill: none;
      stroke: brown;
      pointer-events: none;
    }
    
    .link {
      stroke: #999;
    }
    .node text {
      pointer-events: none;
      font: 10px sans-serif;
    }
    
    path.link {
      fill: none;
      stroke: #666;
      stroke-width: 1.5px;
    }
    <script src="https://cdnjs.cloudflare.com/ajax/libs/d3/3.4.11/d3.min.js"></script>

    【讨论】:

    • 很好的答案,一如既往。我只是更改对象的名称,例如var obj ={}。既然你称它为map,我们期待的是一张真实的地图,比如var map = new Map()var map = d3.map()。顺便说一句,使用真实的地图比使用对象要快,但是这里的数据太短了,可能没有区别。
    【解决方案2】:

    你可以这样做:https://codepen.io/anon/pen/yqJoje?editors=0011

    我基本上过滤节点数组以查找与目标和源相关的 x 和 y 数据,然后将它们分配给一个值以用于路径分配。相关代码在这里:

    function tick() {
      link.attr("d", function(d) {
        var curData = {
          target: nodes.filter(function (n) {
                    if (n.id == d.target) {
                      return { x: d.x, y: d.y };
                    }
                  })[0],
          source: nodes.filter(function (n) { 
                    if (n.id == d.source) {
                      return { x: d.x, y: d.y };
                    }
                  })[0]
        };
               ...
               ...
    }
    

    注意:不确定在 tick 函数中通过数组过滤是否是个好主意,因为如果生成大量节点,它可能会导致性能问题。 tick 函数被调用了很多。

    【讨论】:

      猜你喜欢
      • 2014-07-22
      • 1970-01-01
      • 1970-01-01
      • 2014-12-18
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2016-02-21
      相关资源
      最近更新 更多