我正在尝试在此jsfiddle
中添加删除节点功能 refresh
方法向每个圈子添加了on("mousedown", mousedownNode)
事件,但是当我单击节点GW2
时,它会删除DB
节点。我发现mousedownNode
方法删除了正确的节点,但是刷新方法中的node = node.data(nodes);
混乱了。我不确定如何解决此问题。
function mousedownNode(d, i) {
nodes.splice(i, 1);
links = links.filter(function(l) {
return l.source !== d && l.target !== d;
});
d3.event.stopPropagation();
refresh();
}
编辑
我有一个将节点名索引到索引映射的映射:
var map = {}
nodes.forEach(function(d,i){
map[d.id] = i;
})
links.forEach(function(d) {
d.source = map[d.source];
d.target = map[d.target];
})
答案 0 :(得分:2)
正如您所说,正确的节点将被删除,并且绑定到<g class="node"></g>
的数据似乎也是正确的,但是您不是更新节点文本。
这是您拥有的:
nodeEnter
.append("text")
.attr("dx", 12)
.attr("dy", ".35em")
.text(function(d) {
return d.id;
});
这就是我将其更改为:
nodeEnter
.append("text")
.attr("dx", 12)
.attr("dy", ".35em");
node.select('text')
.text(function(d) {
return d.id;
});
您还可以通过将nodeText分配给像这样的变量var nodeText = ...
这很重要,当您说删除节点后调用刷新时,exit()
选择有效,但现有节点不会自行更新。希望这是有道理的。阅读How Selections Work
JSFIDDLE和以下代码段:
编辑:删除可拖动的SVG元素会导致错误。要解决此问题,请将退出选择行更改为:
node.exit().on('mousedown.drag', null).remove();
在删除元素之前先删除拖动事件绑定。
rect {
fill: none;
pointer-events: all;
}
.node {
fill: #000;
}
.cursor {
fill: green;
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://d3js.org/d3.v3.min.js"></script>
<button id="ref" onclick="refresh()">refresh </button>
<script>
//setInterval(refresh, 15000);
function addNodeCanvas(nodeName,g) {
var node = { x: 900, y: 900, id: nodeName, grp:g };
var n = nodes.push(node);
console.log(node);
refresh();
}
function addLinkCanvas(idSrc, idTarget) {
if (idSrc != idTarget) {
var s = {}, t = {};
nodes.forEach(function(curNode) {
if (typeof curNode.id != "undefined") {
if (curNode.id == idSrc) { s = curNode; }
if (curNode.id == idTarget) { t = curNode; }
}
});
//console.log( { s,t});
links.push({ source: s, target: t });
};
refresh();
}
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: "GW1", x: 200, y: 110 },
{ id: "GW", x: 200, y: 110 },
{ id: "GW2", x: 200, y: 110 },
{ id: "DB", x: 100, y: 110 }
]
var width = 600,
height = 400,
radius = 8;
var map = {}
nodes.forEach(function(d,i){
map[d.id] = i;
})
links.forEach(function(d) {
d.source = map[d.source];
d.target = map[d.target];
})
var force = d3.layout
.force()
.size([width, height])
.nodes(nodes) .links(links)
.linkDistance(50)
.charge(-100)
.on("tick", tick);
var svg = d3
.select("body")
.append("svg")
.attr("width", width)
.attr("height", height);
// build the arrow.
var arrows = svg
.append("svg:defs")
.selectAll("marker")
.data(["arrow"]) // Different link/path types can be defined here
.enter()
.append("svg:marker") // This section adds in the arrows
.attr("id", String)
.attr("viewBox", "0 -5 10 10")
.attr("refX", 10)
.attr("refY", -1)
.attr("markerWidth", 4)
.attr("markerHeight", 4)
.attr("orient", "auto")
.append("svg: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");
function tick() {
link.attr("d", function(d) {
var dx = d.target.x - d.source.x,
dy = d.target.y - d.source.y;
var angle = Math.atan2(dy, dx);
var offsetX = radius * Math.cos(angle);
var offsetY = radius * Math.sin(angle);
dr = Math.sqrt(dx * dx + dy * dy);
return (
"M" +
(d.source.x + offsetX) +
"," +
(d.source.y + offsetY) +
"A" +
dr +
"," +
dr +
" 0 0,1 " +
(d.target.x - offsetX) +
"," +
(d.target.y - offsetY)
);
});
node.attr("transform", function(d) {
return "translate(" + d.x + "," + d.y + ")";
});
}
function mousedownNode(d, i) {
nodes.splice(i, 1);
links = links.filter(function(l) {
return l.source !== d && l.target !== d;
});
d3.event.stopPropagation();
refresh();
}
refresh();
function refresh() {
link = link.data(links);
link
.enter()
.append("path")
.attr("class", "link")
.attr("marker-end", "url(#arrow)");
link.exit().remove();
node = node.data(nodes);
var nodeEnter = node
.enter()
.insert("g")
.attr("class", "node")
.on("mousedown", mousedownNode)
.call(force.drag);
nodeEnter
.append("image")
.attr("xlink:href", "https://github.com/favicon.ico")
.attr("x", -8)
.attr("y", -8)
.attr("width", 16)
.attr("height", 16);
nodeEnter
.append("text")
.attr("dx", 12)
.attr("dy", ".35em");
node.select('text')
.text(function(d) {
return d.id;
});
node.exit().on('mousedown.drag', null).remove();
force.start();
}
</script>
答案 1 :(得分:0)
我没有深入研究,因为我觉得问题对我来说很明显。我无法确切说明GW
节点为何返回,但是我认为这与您的数据绑定不正确有关。
进行数据连接时,基本上是在告诉D3
键入索引。现在,如果它不是索引中的最后一个节点,则将重新使用DOM中原始的GW
节点,因为索引将偏移1。
node = node.data(nodes);
如果您将其更改为输入字段
node = node.data(nodes, d => d.id);
您会发现自己的问题消失了。