5

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

然而,由于 SVG 分层元素的方式,新链接绘制在旧节点上,所以我将节点作为圆圈并在它们之间绘制线条,任何添加的新节点都会在旧节点上的圆圈顶部绘制。见下图:

分层问题

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

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

jsfiddle 上的演示 - 请查看以下部分

draw() {
   ...
}

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

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

有任何想法吗?

4

3 回答 3

4

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

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

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

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

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

于 2013-05-29T13:49:51.333 回答
3

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

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 的答案已被标记为答案)

于 2013-05-29T15:08:19.967 回答
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>

于 2016-05-18T05:56:25.490 回答