Javascript d3js强制布局,在节点上隐藏/取消隐藏单击展开后错位节点
我正在尝试使用d3和force布局创建一个图形。我已使用以下示例开始: 我还需要图像和标签,所以我看了这个例子,想知道如何添加它们 根据用户与节点的交互,我的图也会变大,因此如果用户单击没有子节点的节点,ajax请求将转到后端服务以请求更多节点。该图将用作关系发现应用程序。我创建了以下JSFIDLE来了解我要实现的目标 它工作得比较好,但我有一个恼人的问题:当向图中添加新节点时,旧节点不知何故被放错了位置。例如,我从3个节点开始,根节点是“flare”。其他两个节点是“动画”和“分析”。在我的示例中,每当单击当前没有子节点的节点时,子节点将始终是“x”、“y”、“z”、“t”。展开几个节点后,您将看到“动画”或“分析”未链接到根节点“flare”,而是链接到其他一些节点(x、y、z、t)。有时,如果展开x或y或z或t,则子节点具有重复的x或y或z或t。如果单击“flare”隐藏整个图形,然后重新打开“flare”,您将看到节点已正确链接和命名 我似乎不明白为什么会这样。有人能在这里说明一下吗?我还是d3新手,觉得它很有趣,但这些问题太烦人了 代码如下:Javascript d3js强制布局,在节点上隐藏/取消隐藏单击展开后错位节点,javascript,json,svg,d3.js,force-layout,Javascript,Json,Svg,D3.js,Force Layout,我正在尝试使用d3和force布局创建一个图形。我已使用以下示例开始: 我还需要图像和标签,所以我看了这个例子,想知道如何添加它们 根据用户与节点的交互,我的图也会变大,因此如果用户单击没有子节点的节点,ajax请求将转到后端服务以请求更多节点。该图将用作关系发现应用程序。我创建了以下JSFIDLE来了解我要实现的目标 它工作得比较好,但我有一个恼人的问题:当向图中添加新节点时,旧节点不知何故被放错了位置。例如,我从3个节点开始,根节点是“flare”。其他两个节点是“动画”和“分析”。在我的
var w = 960,
h = 800,
node,
link,
root;
var force = d3.layout.force()
.charge(-1000)
.size([w, h]);
var vis = d3.select("#chart").append("svg:svg")
.attr("width", w)
.attr("height", h);
d3.json("data.json", function (json) {
root = json;
update();
});
function update() {
var nodes = flatten(root);
nodes.reverse();
nodes = nodes.sort(function (a, b) {
return a.index - b.index;
});
var links = d3.layout.tree().links(nodes);
console.log(nodes);
// Restart the force layout.
force
.nodes(nodes)
.links(links)
.linkDistance(55)
.start();
var link = vis.selectAll(".link")
.data(links);
link.enter().append("line")
.attr("class", "link");
link.exit().remove();
var node = vis.selectAll("g.node")
.data(nodes)
var groups = node.enter().append("g")
.attr("class", "node")
.attr("id", function (d) {
return d.id
})
.on('click', click)
.call(force.drag);
groups.append("image")
.attr("xlink:href", "https://github.com/favicon.ico")
.attr("x", -8)
.attr("y", -8)
.attr("width", 16)
.attr("height", 16);
groups.append("text")
.attr("dx", 12)
.attr("dy", "0.35em")
.style("font-size", "10px")
.text(function (d) {
console.log(d);
return d.name
});
node.exit().remove();
force.on("tick", function () {
link.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;
});
node.attr("transform", function (d) {
return "translate(" + d.x + "," + d.y + ")";
});
});
}
// Color leaf nodes orange, and packages white or blue.
function color(d) {
return d._children ? "#3182bd" : d.children ? "#c6dbef" : "#fd8d3c";
}
// Toggle children on click.
function click(d) {
console.log(d);
if (d.children) {
d._children = d.children;
d.children = null;
update();
} else if (d._children) {
d.children = d._children;
d._children = null;
update();
}
else {
d3.json("expand.json", function (json) {
d.children = json.children;
update();
})
}
}
// Returns a list of all nodes under the root.
function flatten(root) {
var nodes = [], i = 0;
function recurse(node) {
if (node.children) node.children.forEach(recurse);
if (!node.id) node.id = ++i;
nodes.push(node);
}
recurse(root);
return nodes;
}
下面是我请求的2个json文件:
data.json
{
"name": "flare",
"id" : "flare",
"children": [
{
"name": "analytics",
"id": "analytics"
},
{
"name": "animate",
"id": "animate"
}
]
}
和expand.json
{"children": [
{
"name": "x",
"id": "x",
"size": 1983
},
{
"name": "y",
"id": "y",
"size": 2047
},
{
"name": "z",
"id": "z",
"size": 1375
},
{
"name": "t",
"id": "t",
"size": 1375
}
]}
PS:我必须对节点数组进行排序,否则图形会发生不好的事情,我无法理解为什么 这里是工作解决方案。我认为问题在于您声明id和基于数组索引对其排序的方式。您应该让展平代码声明id,然后根据给定的id对其进行排序。同样,在递归函数中,您可能希望先声明父函数,然后再声明子函数
function recurse(node) {
if(!node.id) node.id = ++i;
nodes.push(node);
if (node.children) node.children.forEach(recurse);
}
从约格什的回答开始,我设法找到了解决这个问题的办法。下面是需要添加到update()函数中的代码
var currentNodes = force.nodes();
var nodes = flatten(root);
var actualNodes = [];
var values = currentNodes.map(function(obj) { return obj.name});
var newNodesValues = nodes.map(function(obj) { return obj.name });
for(var i = 0; i < currentNodes.length; i++) {
if(newNodesValues.indexOf(currentNodes[i].name) !== -1) {
actualNodes.push(currentNodes[i]);
}
}
for(var i = 0; i < nodes.length; i++) {
if(values.indexOf(nodes[i].name) == -1) {
actualNodes.push(nodes[i]);
}
}
nodes = actualNodes;
var links = d3.layout.tree().links(nodes);
// Restart the force layout.
force
.nodes(nodes)
.links(links)
.linkDistance(55)
.start();
var currentNodes=force.nodes();
var节点=展平(根);
var实际节点=[];
var values=currentNodes.map(函数(obj){return obj.name});
var newNodesValues=nodes.map(函数(obj){return obj.name});
对于(var i=0;i
以下几点可以做到:
var i = 0;
data()的第二个参数是一个回调函数,当使用数据调用时,该函数返回将每个DOM节点绑定到其相应数据的键。当您不提供这样一个函数时,d3没有任何选项,只能使用索引将数据绑定到DOM节点
没有帮到我,问题确实出在递归函数上,但我不知道为什么。最有可能的是,力图需要以某种方式排列节点……但似乎无法找出哪一个节点。你能告诉我我的小提琴中的解决方案有什么问题吗?我认为它可以工作。jsfiddle似乎可以工作,但是当您获取源代码并在浏览器中运行它们时,您会发现它不能工作。奇怪的无论如何,你也应该看看x,y,z,t节点,它们有时会被放错位置。我试过你的解决办法,但没能奏效。我真的很感谢你的帮助,因为从你所说的开始,我缩小了问题范围,并找到了解决办法。问题在于递归函数每次都不在同一位置插入节点,因此图形会变得混乱。那么解决方案是什么呢?请查看我的答案。虽然这是一个残酷的解决方案,但它似乎有效。
var link = vis.selectAll(".link")
.data(links, function (d) {
return d.id || (d.id = ++i);
});
var node = vis.selectAll("g.node")
.data(nodes, function (d) {
return d.id || (d.id = ++i);
});