D3 Linking nodes based on names rather than index

后端 未结 2 653
南笙
南笙 2020-12-11 22:17

I am trying to link the nodes based on Id rather than the index in this codepen, I am not able to figure out how to do it.

The following works:

var l         


        
相关标签:
2条回答
  • 2020-12-11 23:11

    A D3v3 force layout doesn't support named nodes in the links. I'd suggest d3v4 or v5 if you want to use them without requiring any extra work.

    But, as with REEE's answer, you can achieve the same effect by altering your code and keeping v3. Rather than alter the tick, I'm altering the link array prior to initiating the array:

    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
    })
    

    Here's a forked plunkr, or a snippet below (I've moved your node array into its own variable - yes, it's called nodes and gets overwritten later):

    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>

    0 讨论(0)
  • 2020-12-11 23:18

    You can do it like this: https://codepen.io/anon/pen/yqJoje?editors=0011

    I basically filter the nodes array to look for the x and y data relating to the target and source, then assign these to a value for use in your path assignment. Relevant code is here:

    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]
        };
               ...
               ...
    }
    

    Note: Not sure if filtering through an array in the tick function is a good idea as it is probably going to cause performance issues down the line if a lot of nodes are generated. The tick function is being called a lot.

    0 讨论(0)
提交回复
热议问题