我正在努力为这个问题提供一个可能的解决方案:d3 force layout with node groups
我有一个像这样的JSON:
{
nodes: [
{
label: "x"
inputs: [],
outputs: ["a"]
},
{
label: "y"
inputs: ["b"],
outputs: []
},
...
],
links: [
{
source: 0, //x
sourceIndex: 0, //x.outputs.a
target: 1, //y
targetIndex: 0 //y.inputs.b
},
...
]
}
我想为每个节点构建一个SVG:
<g class="component worker" transform="translate(0,0)">
<use class="container" xlink:href="#container"/>
<use class="icon" xlink:href="#worker"/>
<text class="label" text-anchor="middle" alignment-baseline="middle" dy="40">Worker</text>
<g class="input" transform="translate(-31,-16)">
<use class="port" xlink:href="#port">
<title>stdin</title>
</use>
<use y="8" class="port" xlink:href="#port"/>
<use y="16" class="port" xlink:href="#port"/>
<use y="24" class="port" xlink:href="#port"/>
<use y="32" class="port" xlink:href="#port"/>
</g>
<g class="output" transform="translate(31,16)">
<use class="port" xlink:href="#port">
<title>stdout</title>
</use>
<use y="-8" class="port" xlink:href="#port"/>
<use y="-16" class="port" xlink:href="#port"/>
<use y="-24" class="port" xlink:href="#port"/>
<use y="-32" class="port" xlink:href="#port"/>
</g>
</g>
我有以下js代码来构建SVG:
var node = svg.selectAll(".node").data(this.data.nodes).enter().append("g").attr({
"class": function (d) {
return d.groups.join(" ");
}
}).call(force.drag);
node.append("use").attr({
"class": "container",
"xlink:href": "#container"
});
node.append("use").attr({
"class": "icon",
"xlink:href": function (d) {
return d.icon;
}
});
node.append("text").attr({
"class": "label",
"text-anchor": "middle",
"alignment-baseline": "middle",
dy: 40
}).text(function (d) {
return d.label
});
node.append("g").attr({
"class": "input",
transform: "translate(-31,-16)"
});
node.append("g").attr({
"class": "output",
transform: "translate(31,16)"
});
现在我缺少的是输入和输出。我不知道如何迭代节点的那些属性以及如何使用d3.js的流畅接口逐个追加它们。我检查了d3: Optionally add child elements based on node content in force layout但过滤在这里不起作用,因为我们讨论的是迭代集合,而不是检查属性的存在。有什么建议吗?
答案 0 :(得分:0)
您可以使用嵌套选择。
我假设输入要为节点数据中的inputs
数组的每个成员附加一个项目,同样也为输出附加。
例如,输入:
node.append("g").attr({
"class": "input",
transform: "translate(-31,-16)"
})
.selectAll('SOME_THING')
.data(function(d) { return d.inputs; })
.enter().append('SOME_THING')
其中'SOME_THING'
是您要追加的元素(可能是<use>
,尽管从您的示例输出中可以清楚地看到这些元素会被映射到)。
答案 1 :(得分:0)
我用each()
解决了问题:
node.each(function (d) {
var node = d3.select(this);
var inputs = node.append("g").attr({
"class": "input",
transform: "translate(-31,-16)"
});
d.inputs.forEach(function (name, index) {
inputs.append("use").attr({
y: -8 * index,
"xlink:href": "#port"}).append("title").text(name);
});
});
但是我会使用jshanley的解决方案,我想那是d3方式......: - )
var inputs = node.append("g").attr({
"class": "input",
transform: "translate(-31,-16)"
});
inputs.selectAll("use").data(function (d) {
return d.inputs;
}).enter().append("use").attr({
y: function (d, index) {
return -8 * index;
},
"xlink:href": "#port"
}).append("title").text(String);