Space 在 D3 力布局中均匀地围绕根节点输出节点
Space out nodes evenly around root node in D3 force layout
我刚开始学习 D3,所以如果有人对我可能不会做的事情有任何一般性建议 correctly/optimally,请告诉我:)
我正在尝试创建一个力导向图,其中节点围绕中心根节点(以较大的尺寸标注)均匀分布(或足够接近)。
这是我尝试实现的布局示例(我知道它不会每次都一样):
我有下图:
var width = $("#theVizness").width(),
height = $("#theVizness").height();
var color = d3.scale.ordinal().range(["#ff0000", "#fff000", "#ff4900"]);
var force = d3.layout.force()
.charge(-120)
.linkDistance(30)
.size([width, height]);
var svg = d3.select("#theVizness").append("svg")
.attr("width", width)
.attr("height", height);
var loading = svg.append("text")
.attr("class", "loading")
.attr("x", width / 2)
.attr("y", height / 2)
.attr("dy", ".35em")
.style("text-anchor", "middle")
.text("Loading...");
/*
ForceDirectData.json
{
"nodes":[
{"name":"File1.exe","colorGroup":0},
{"name":"File2.exe","colorGroup":0},
{"name":"File3.exe","colorGroup":0},
{"name":"File4.exe","colorGroup":0},
{"name":"File5.exe","colorGroup":0},
{"name":"File6.exe","colorGroup":0},
{"name":"File7.exe","colorGroup":0},
{"name":"File8.exe","colorGroup":0},
{"name":"File8.exe","colorGroup":0},
{"name":"File9.exe","colorGroup":0}
],
"links":[
{"source":1,"target":0,"value":10},
{"source":2,"target":0,"value":35},
{"source":3,"target":0,"value":50},
{"source":4,"target":0,"value":50},
{"source":5,"target":0,"value":65},
{"source":6,"target":0,"value":65},
{"source":7,"target":0,"value":81},
{"source":8,"target":0,"value":98},
{"source":9,"target":0,"value":100}
]
}
*/
d3.json("https://dl.dropboxusercontent.com/u/5772230/ForceDirectData.json", function (error, json) {
var nodes = json.nodes;
force.nodes(nodes)
.links(json.links)
.linkDistance(function (d) {
return d.value * 1.5;
})
.charge(function(d){
var charge = -500;
if (d.index === 0) charge = 0;
return charge;
})
.friction(0.4);
var link = svg.selectAll(".link")
.data(json.links)
.enter().append("line")
.attr("class", "link")
.style("stroke-width", 1);
var files = svg.selectAll(".file")
.data(json.nodes)
.enter().append("circle")
.attr("class", "file")
.attr("r", 10)
.attr("fill", function (d) {
return color(d.colorGroup);
});
var totalNodes = files[0].length;
files.append("title")
.text(function (d) { return d.name; });
force.start();
for (var i = totalNodes * totalNodes; i > 0; --i) force.tick();
nodes[0].x = width / 2;
nodes[0].y = height / 2;
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; });
files.attr("cx", function (d) { return d.x; })
.attr("cy", function (d) { return d.y; })
.attr("class", function(d){
var classString = "file"
if (d.index === 0) classString += " rootFile";
return classString;
})
.attr("r", function(d){
var radius = 10;
if (d.index === 0) radius = radius * 2;
return radius;
});
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; });
files.attr("cx", function(d) { return d.x; })
.attr("cy", function(d) { return d.y; });
});
loading.remove();
});
JSFiddle
我已经尝试用 charge()
方法接近这个。我以为给除根节点以外的每个节点更高的电荷就可以做到这一点,但事实并非如此。
如何使子节点均匀分布在根节点周围?
是的,强制布局是适合您这种情况的完美工具。
你只需要改变一点布局的初始化,像这样
force.nodes(nodes)
.links(json.links)
.charge(function(d){
var charge = -500;
if (d.index === 0) charge = 10 * charge;
return charge;
});
瞧瞧
解释。我不得不删除 friction
和 linkDistance
的设置,因为它们会以不好的方式影响展示位置。根节点的 charge
大 10 倍,因此所有其他节点都被显性地推离根节点。其他节点也相互排斥,最终达到完美对称。
Jsfiddle 是 here。
我从您的代码中看到您试图通过利用依赖于数据的 linkDistance
来影响与根节点和其他节点的距离。但是,为此目的使用 linkStrength
可能更好(尽管违反直觉),例如
force.nodes(nodes)
.links(json.links)
.linkStrength(function (d) {
return d.value / 100.0;
})
.charge(function(d){
var charge = -500;
if (d.index === 0) charge = 10 * charge;
return charge;
});
但你需要试验一下。
要居中和固定根节点,可以使用这个
nodes[0].fixed = true;
nodes[0].x = width / 2;
nodes[0].y = height / 2;
但在布局初始化之前,就像在 this Jsfiddle 中一样。
我刚开始学习 D3,所以如果有人对我可能不会做的事情有任何一般性建议 correctly/optimally,请告诉我:)
我正在尝试创建一个力导向图,其中节点围绕中心根节点(以较大的尺寸标注)均匀分布(或足够接近)。
这是我尝试实现的布局示例(我知道它不会每次都一样):
我有下图:
var width = $("#theVizness").width(),
height = $("#theVizness").height();
var color = d3.scale.ordinal().range(["#ff0000", "#fff000", "#ff4900"]);
var force = d3.layout.force()
.charge(-120)
.linkDistance(30)
.size([width, height]);
var svg = d3.select("#theVizness").append("svg")
.attr("width", width)
.attr("height", height);
var loading = svg.append("text")
.attr("class", "loading")
.attr("x", width / 2)
.attr("y", height / 2)
.attr("dy", ".35em")
.style("text-anchor", "middle")
.text("Loading...");
/*
ForceDirectData.json
{
"nodes":[
{"name":"File1.exe","colorGroup":0},
{"name":"File2.exe","colorGroup":0},
{"name":"File3.exe","colorGroup":0},
{"name":"File4.exe","colorGroup":0},
{"name":"File5.exe","colorGroup":0},
{"name":"File6.exe","colorGroup":0},
{"name":"File7.exe","colorGroup":0},
{"name":"File8.exe","colorGroup":0},
{"name":"File8.exe","colorGroup":0},
{"name":"File9.exe","colorGroup":0}
],
"links":[
{"source":1,"target":0,"value":10},
{"source":2,"target":0,"value":35},
{"source":3,"target":0,"value":50},
{"source":4,"target":0,"value":50},
{"source":5,"target":0,"value":65},
{"source":6,"target":0,"value":65},
{"source":7,"target":0,"value":81},
{"source":8,"target":0,"value":98},
{"source":9,"target":0,"value":100}
]
}
*/
d3.json("https://dl.dropboxusercontent.com/u/5772230/ForceDirectData.json", function (error, json) {
var nodes = json.nodes;
force.nodes(nodes)
.links(json.links)
.linkDistance(function (d) {
return d.value * 1.5;
})
.charge(function(d){
var charge = -500;
if (d.index === 0) charge = 0;
return charge;
})
.friction(0.4);
var link = svg.selectAll(".link")
.data(json.links)
.enter().append("line")
.attr("class", "link")
.style("stroke-width", 1);
var files = svg.selectAll(".file")
.data(json.nodes)
.enter().append("circle")
.attr("class", "file")
.attr("r", 10)
.attr("fill", function (d) {
return color(d.colorGroup);
});
var totalNodes = files[0].length;
files.append("title")
.text(function (d) { return d.name; });
force.start();
for (var i = totalNodes * totalNodes; i > 0; --i) force.tick();
nodes[0].x = width / 2;
nodes[0].y = height / 2;
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; });
files.attr("cx", function (d) { return d.x; })
.attr("cy", function (d) { return d.y; })
.attr("class", function(d){
var classString = "file"
if (d.index === 0) classString += " rootFile";
return classString;
})
.attr("r", function(d){
var radius = 10;
if (d.index === 0) radius = radius * 2;
return radius;
});
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; });
files.attr("cx", function(d) { return d.x; })
.attr("cy", function(d) { return d.y; });
});
loading.remove();
});
JSFiddle
我已经尝试用 charge()
方法接近这个。我以为给除根节点以外的每个节点更高的电荷就可以做到这一点,但事实并非如此。
如何使子节点均匀分布在根节点周围?
是的,强制布局是适合您这种情况的完美工具。
你只需要改变一点布局的初始化,像这样
force.nodes(nodes)
.links(json.links)
.charge(function(d){
var charge = -500;
if (d.index === 0) charge = 10 * charge;
return charge;
});
瞧瞧
解释。我不得不删除 friction
和 linkDistance
的设置,因为它们会以不好的方式影响展示位置。根节点的 charge
大 10 倍,因此所有其他节点都被显性地推离根节点。其他节点也相互排斥,最终达到完美对称。
Jsfiddle 是 here。
我从您的代码中看到您试图通过利用依赖于数据的 linkDistance
来影响与根节点和其他节点的距离。但是,为此目的使用 linkStrength
可能更好(尽管违反直觉),例如
force.nodes(nodes)
.links(json.links)
.linkStrength(function (d) {
return d.value / 100.0;
})
.charge(function(d){
var charge = -500;
if (d.index === 0) charge = 10 * charge;
return charge;
});
但你需要试验一下。
要居中和固定根节点,可以使用这个
nodes[0].fixed = true;
nodes[0].x = width / 2;
nodes[0].y = height / 2;
但在布局初始化之前,就像在 this Jsfiddle 中一样。