我正在构建一个烦人的问题,我的d3强制定向地图我正在构建我最初使用我需要的节点和链接呈现页面,然后定期通过ajax检查新信息。当我需要一个新节点和链接时,我绘制它们,这很好
然而,由于SVG图层元素的方式,新链接在旧节点上绘制,因此我将节点作为圆形并在它们之间绘制线条,添加的任何新节点都绘制在旧节点上的圆顶上。见下图:
layering issue http://i40.tinypic.com/4fx25j.gif
(http://i40.tinypic.com/4fx25j.gif)
我知道这在技术上不是d3问题,但必须有办法解决这个问题。我确实尝试删除所有的圆圈并重新绘制它们,但问题是它附加的svg:g节点在图层中太低,所以它仍然被绘制。
在jsfiddle演示 - 请看以下部分
draw() {
...
}
因为这就是魔术发生的地方。 http://jsfiddle.net/zuzzy/uwAhy/
我使用5秒计时器模拟了ajax,演示更容易。
有什么想法吗?
答案 0 :(得分:4)
据我所知,你只能通过它在DOM中的位置来控制SVG元素的深度。
那么可能对您有用的是创建两个组<g id='lines'>
和<g id='circles'>
。
当你append
你的元素时,所有的行都应该添加到第一组,所有的圆都添加到第二组。
您可能需要更改添加元素的方式,但只要您确保lines
组出现在circles
组之前,那么您应该是金色的。
如果这完全不适合您的实施,我道歉。我遇到了一个非常类似的问题,发现对我来说唯一的解决方法是首先绘制'下'元素。
答案 1 :(得分: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");
并在绘制链接和vis绘制圆圈时引用linkvis。
(NB我知道这应该是一个评论,但我不能适应它,我认为它可能对某人有帮助。@保罗的回答已被标记为答案)
答案 2 :(得分:2)
解决此问题的另一种方法是使用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;
});
}
&#13;
<script src="https://cdnjs.cloudflare.com/ajax/libs/d3/3.4.11/d3.min.js"></script>
&#13;