【问题标题】:d3 / svg layering issue when adding new nodes after a time一段时间后添加新节点时的d3 / svg分层问题
【发布时间】:2019-09-03 21:44:46
【问题描述】:

我正在构建的 d3 强制定向地图存在一个烦人的问题,我最初在该地图上使用所需的节点和链接渲染页面,然后通过 ajax 定期检查新信息。当我需要一个新节点和链接时,我会绘制它们,这很好

但是,由于 SVG 分层元素的方式,新链接会在旧节点上绘制,因此在我将节点作为圆圈并在它们之间画线的情况下,添加的任何新节点都会在旧节点上的圆圈顶部绘制。见下图:

(http://i40.tinypic.com/4fx25j.gif)

我知道这在技术上不是 d3 问题,但必须有解决此问题的方法。我确实尝试删除所有圆圈并重新绘制它们,但问题是它所附加的 svg:g 节点在图层中太低,所以它仍然被绘制。

在 jsfiddle 上演示 - 请看以下部分

draw() {
   ...
}

因为这就是魔法发生的地方。 http://jsfiddle.net/zuzzy/uwAhy/

我使用 5 秒计时器模拟了 ajax,演示更容易。

有什么想法吗?

【问题讨论】:

    标签: javascript svg d3.js


    【解决方案1】:

    据我所知,您只能通过 SVG 元素在 DOM 中的位置来控制它的深度。

    因此,可能对您有用的是创建两个组<g id='lines'><g id='circles'>

    当你append你的元素时,所有的线都应该添加到第一组,所有的圆圈都应该添加到第二组。

    您可能必须更改添加元素的方式,但只要您确保 lines 组出现在 circles 组之前,那么您应该是黄金。

    如果这完全不适合您的实施,我深表歉意。我遇到了一个非常相似的问题,发现对我来说唯一的解决方案是先绘制“较低”的元素。

    【讨论】:

    • 我会玩一会然后回复你。我不知道你可以这样分组,这听起来很合理
    【解决方案2】:

    第一次工作!我已经将所有元素归为一个,所以我只是替换了:

    var vis = d3.select("body")    
    .append("svg:svg")  
    .attr("pointer-events", "all");  
    .append('svg:g')
    

    我使用 vis.xxxx 来渲染链接和圆圈,

    var vis = d3.select("body") 
    .append("svg:svg")   
    .attr("pointer-events", "all");  
    
    var linkvis = vis.append('svg:g')  
    .attr("id","link_elements");   
    
    vis = vis.append('svg:g')   
    .attr("id","node_elements");   
    

    并在绘制链接和绘制圆圈时引用linkvis。

    (注意,我知道这应该是一条评论,但我不适合它,我认为它可能对某人有帮助。@Paul 的答案已被标记为答案)

    【讨论】:

      【解决方案3】:

      解决此问题的另一种方法是使用insert 方法,如以下代码所示。

       link.enter().insert("line",".node"); //Inserts link element before the first DOM element with class node.
      

      发布只是因为这可能对搜索此问题的解决方案的其他用户有所帮助。

      //Settings:
      //width, height and the default radius of the circles
      var w = 1024,
        h = 768,
        r = 10;
      
      
      
      
      //test data - usually this is recieved via ajax
      //Initial Data:
      var hosts = eval({
        "ITEM003": {
          "name": "ITEM003",
          "parents": [],
          "status": 0,
          "hostgroup": "Secure"
        },
        "ITEM004": {
          "name": "ITEM004",
          "parents": [],
          "status": 0,
          "hostgroup": "Secure"
        },
        "CORE": {
          "name": "CORE",
          "parents": ["ITEM004", "ITEM003"],
          "status": 0,
          "hostgroup": "DMZ"
        }
      });
      
      var mylinks = eval({
        "0": ["CORE", "ITEM004"],
        "1": ["CORE", "ITEM003"]
      });
      
      //Data after update
      var updated_hosts = eval({
        "ITEM003": {
          "name": "ITEM003",
          "parents": [],
          "status": 0,
          "hostgroup": "Secure"
        },
        "ITEM004": {
          "name": "ITEM004",
          "parents": [],
          "status": 0,
          "hostgroup": "Secure"
        },
        "CORE": {
          "name": "CORE",
          "parents": ["ITEM004", "ITEM003"],
          "status": 0,
          "hostgroup": "DMZ"
        },
        "REMOTE": {
          "name": "REMOTE",
          "parents": [],
          "status": 0,
          "hostgroup": ""
        }
      });
      
      var updated_mylinks = eval({
        "0": ["CORE", "ITEM004"],
        "1": ["CORE", "ITEM003"],
        "2": ["CORE", "REMOTE"]
      });
      
      
      
      //I define these here so they carry between functions - not really necessary in this jsfiddle probably
      window.link = undefined;
      window.node = undefined;
      
      
      
      
      //make up my node object
      window.nodeArray = [];
      window.node_hash = [];
      
      for (var key in hosts) {
        var a = {
          id: "node_" + hosts[key].name,
          labelText: hosts[key].name,
          status: hosts[key].status,
          hostgroup: hosts[key].hostgroup,
          class: "node realnode",
          iconimage: hosts[key].iconimage,
          added: true
        };
        nodeArray.push(a);
        node_hash[key] = a;
      }
      
      //make up my link object
      window.linkArray = [];
      
      for (var key in mylinks) {
        var linkcolor = "#47CC60";
      
        var a = {
          source: node_hash[mylinks[key][0]],
          target: node_hash[mylinks[key][1]],
          color: linkcolor,
          class: "link reallink"
        };
        linkArray.push(a);
      }
      
      
      //make up my node text objects
      //these are just more nodes with a different class
      //we will append text to them later
      //we also add the links to the linkArray now to bind them to their real nodes
      window.text_hash = [];
      
      for (var key in hosts) {
        var a = {
          id: "label_" + hosts[key].name,
          text: hosts[key].name,
          color: "#ffffff",
          size: "6",
          class: "node label",
          added: true
        };
        nodeArray.push(a);
        text_hash[key] = a;
      }
      
      //because the text labels are in the same order as the
      //original nodes we know that node_hash[0] has label text_hash[0]
      //it doesn't matter which we iterate through here
      
      for (var key in text_hash) {
        var a = {
          source: node_hash[key],
          target: text_hash[key],
          class: "link label"
        };
        linkArray.push(a);
      }
      
      
      
      //set up the environment in a div called graph using the settings baove 
      window.vis = d3.select("body")
        .append("svg:svg")
        .attr("height", 500)
        .attr("width", 500)
        .attr("pointer-events", "all")
        .append('svg:g')
      
      
      
      
      //object to interact with the force libraries in d3
      //the settings here set how the nodes interact
      //seems a bit overcomplicated but it stops the diagram going nuts!
      window.force = d3.layout.force()
        .friction("0.7")
        .gravity(function(d, i) {
          if (d.class == "link reallink") {
            return "0.95";
          } else {
            return "0.1";
          }
      
        })
        .charge(function(d, i) {
          if (d.class == "link reallink") {
            return "-1500";
          } else {
            return "-300";
          }
      
        })
        .linkDistance(function(d) {
          if (d.class == "link reallink") {
            return "120";
          } else {
            return "35";
          }
      
        })
        .linkStrength(function(d) {
          if (d.class == "link reallink") {
            return "8";
          } else {
            return "6";
          }
        })
        .nodes(nodeArray)
        .links(linkArray)
        .on("tick", tick)
      
      node = vis.selectAll(".node");
      link = vis.selectAll(".link");
      
      //create the objects and run it
      draw();
      
      for (key in nodeArray) {
        nodeArray[key].added = false;
      }
      
      //wait 5 seconds then update the diagram TO ADD A NODE
      setTimeout(function() {
        //update the objects
        //vis.selectAll("g.node").data(nodeArray).exit().transition().ease("elastic").remove();
        //vis.selectAll("line").data(linkArray).exit().transition().ease("elastic").remove();
      
      
        var a = {
          id: "node_REMOTE",
          labelText: "REMOTE",
          status: "0",
          hostgroup: "",
          class: "node realnode",
          iconimage: "",
          added: true
        };
        nodeArray.push(a);
        node_hash["REMOTE"] = a;
      
      
      
        var linkcolor = "#47CC60";
        var a = {
          source: node_hash["CORE"],
          target: node_hash["REMOTE"],
          color: linkcolor,
          class: "link reallink"
        };
        linkArray.push(a);
      
      
      
        //make up my node text objects
      
      
        var a = {
          id: "label_REMOTE",
          text: "REMOTE",
          color: "#000000",
          size: "6",
          class: "node label",
          added: true
        };
        nodeArray.push(a);
        text_hash["REMOTE"] = a;
      
      
        var a = {
          source: node_hash["REMOTE"],
          target: text_hash["REMOTE"],
          class: "link label"
        };
        linkArray.push(a);
      
      
        //redraw it
        draw();
      
      }, 5000);
      
      
      
      
      //----- functions for drawing and tick below
      
      
      function draw() {
      
        link = link.data(force.links(), function(d) {
          return d.source.id + "-" + d.target.id;
        });
        node = node.data(force.nodes(), function(d) {
          return d.id;
        });
      
      
        //create the link object using the links object in the json
        //link = vis.selectAll("line").data(linkArray);
        link.enter().insert("line", ".node")
          .attr("stroke-width", '0')
          .transition()
          .duration(1000)
          .ease("bounce")
          .attr("stroke-width", function(d, i) {
            if (d.class == 'link reallink') {
              return '3';
            } else {
              return '0';
            };
          })
          .style("stroke", function(d, i) {
            return d.color;
          })
          .attr("class", function(d, i) {
            return d.class;
          });
      
      
        //node = vis.selectAll("g").data(nodeArray);
        node.enter().append("svg:g")
          .attr("class", function(d) {
            return d.class
          })
          .attr("id", function(d) {
            return d.id
          })
          .call(force.drag);
      
      
        //append to each node an svg circle element
        vis.selectAll(".realnode").filter(function(d) {
            return d.added;
          })
          .append("svg:circle")
          .attr("r", "0")
          .transition()
          .duration(1000)
          .ease("bounce")
          .attr("r", "6")
          .style("fill", "#000000")
          .style("stroke", function(d) {
            return d.color;
          })
          .style("stroke-width", "4");
      
      
      
        //append to each node the attached text desc
        vis.selectAll(".label").filter(function(d) {
            return d.added;
          })
          .append("svg:text")
          .attr("text-anchor", "middle")
          .attr("fill", "black")
          .style("pointer-events", "none")
          .attr("font-size", "9px")
          .attr("font-weight", "100")
          .text(function(d) {
            return d.text;
          })
          .attr("transform", "rotate(180)")
          .transition()
          .duration(1000)
          .ease("bounce")
          .attr("transform", "rotate(0)");
      
      
        node.exit().transition().ease("elastic").remove();
        link.exit().transition().ease("elastic").remove();
      
        //activate it all - initiate the nodes and links
        force.start();
      
      }
      
      
      
      
      
      function tick() {
        node.attr("cx", function(d) {
            return d.x = Math.max(r + 15, Math.min(w - r - 15, d.x));
          })
          .attr("cy", function(d) {
            return d.y = Math.max(r + 15, Math.min(h - r - 15, d.y));
          })
          .attr("transform", function(d) {
            return "translate(" + d.x + "," + d.y + ")";
          });
      
      
        link.data(linkArray).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;
          });
      }
      <script src="https://cdnjs.cloudflare.com/ajax/libs/d3/3.4.11/d3.min.js"></script>

      【讨论】:

        猜你喜欢
        • 2019-01-02
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2017-12-01
        • 2021-05-26
        相关资源
        最近更新 更多