D3 对严格树的有向图有多种布局,例如:
A
|\
B C
/ \
D E
我需要绘制一个不是树的节点层次结构,而是一个有向无环图。这是树布局的问题,因为有几个分支会聚:
A
|\
B C
\|
D
有谁知道一般层次结构的 D3 布局?或者,对现有的树形布局进行一些巧妙的修改?我注意到 GraphVis 可以很好地处理这种情况,但是 D3 生成的图表更适合这里的要求。
D3 对严格树的有向图有多种布局,例如:
A
|\
B C
/ \
D E
我需要绘制一个不是树的节点层次结构,而是一个有向无环图。这是树布局的问题,因为有几个分支会聚:
A
|\
B C
\|
D
有谁知道一般层次结构的 D3 布局?或者,对现有的树形布局进行一些巧妙的修改?我注意到 GraphVis 可以很好地处理这种情况,但是 D3 生成的图表更适合这里的要求。
您可以创建自己的代码,而不必依赖 D3 布局来完成它。
我在 jsFiddle 中提供了一个示例。该示例非常简单,需要进行一些工作以适应更复杂的示例。
该示例也可以通过相对较少的工作来重新处理分层数据。
这是我在 jsFiddle 中使用的代码:
// Sample data set
var json = {
nodes: [{
name: 'A'},
{
name: 'B'},
{
name: 'C'},
{
name: 'D'}],
links: [{
source: 'A',
target: 'B'},
{
source: 'A',
target: 'C'},
{
source: 'B',
target: 'D'},
{
source: 'C',
target: 'D'}
]
};
var vis = d3.select('#vis').attr('transform', 'translate(20, 20)');
// Build initial link elements - Build first so they are under the nodes
var links = vis.selectAll('line.link').data(json.links);
links.enter().append('line').attr('class', 'link').attr('stroke', '#000');
// Build initial node elements
var nodes = vis.selectAll('g.node').data(json.nodes);
nodes.enter().append('g').attr('class', 'node').append('circle').attr('r', 10).append('title').text(function(d) {
return d.name;
});
// Store nodes in a hash by name
var nodesByName = {};
nodes.each(function(d) {
nodesByName[d.name] = d;
});
// Convert link references to objects
links.each(function(link) {
link.source = nodesByName[link.source];
link.target = nodesByName[link.target];
if (!link.source.links) {
link.source.links = [];
}
link.source.links.push(link.target);
if (!link.target.links) {
link.target.links = [];
}
link.target.links.push(link.source);
});
// Compute positions based on distance from root
var setPosition = function(node, i, depth) {
if (!depth) {
depth = 0;
}
if (!node.x) {
node.x = (i + 1) * 40;
node.y = (depth + 1) * 40;
if (depth <= 1) {
node.links.each(function(d, i2) {
setPosition(d, i2, depth + 1);
});
}
}
};
nodes.each(setPosition);
// Update inserted elements with computed positions
nodes.attr('transform', function(d) {
return 'translate(' + d.x + ', ' + d.y + ')';
});
links.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;
});
作为这个例子:“ Force Directed Trees ”说明有一个经常有效的技巧。在示例中,力方向的行为在每个刻度上进行调整,以便节点根据链接的方向略微向上或向下漂移。如图所示,这将对树做得很好,但我发现它也适用于无环图。没有承诺,但它可能会有所帮助。
一般来说,说到树和数据层次结构,您只需要在 B 和 C 的子列表中都有“D”。
创建你的节点列表,确保你有一个唯一的 id 返回,这样“D”就不会出现两次。
vis.selectAll("g.node").data(nodes, function(d) { return d.id; });
然后当你打电话
var links = tree.links(nodes)
你应该让 D 出现两次作为“目标”(B 和 C 分别作为“源”),这导致单个节点“D”有两行。
我能够使用 Dagre(https://github.com/dagrejs/dagre)和 cytoscape的组合来做到这一点
有一个名为 Dagre-D3 的库,您可以将其用作此库的渲染器,因此它看起来像您想要的 D3 解决方案。
查看此小提琴以查看 Dagre 和 Cytoscape 的基本实现:https ://jsfiddle.net/KateJean/xweudjvm/
var cy = cytoscape({
container: document.getElementById('cy'),
elements: {
nodes: [
{ data: { id: '1' } },
{ data: { id: '2' } },
{ data: { id: '3' } },
{ data: { id: '4' } },
{ data: { id: '5' } },
{ data: { id: '6' } },
{ data: { id: '7' } },
{ data: { id: '8' } },
{ data: { id: '9' } },
{ data: { id: '10' } },
{ data: { id: '11' } },
{ data: { id: '12' } },
{ data: { id: '13' } },
{ data: { id: '14' } },
{ data: { id: '15' } },
{ data: { id: '16' } },
{ data: { id: '17' } },
{ data: { id: '18' } }
],
edges: [
{ data: { source: '1', target: '2' } },
{ data: { source: '1', target: '3' } },
{ data: { source: '2', target: '4' } },
{ data: { source: '4', target: '5' } },
{ data: { source: '4', target: '6' } },
{ data: { source: '5', target: '6' } },
{ data: { source: '5', target: '7' } },
{ data: { source: '7', target: '8' } },
{ data: { source: '3', target: '9' } },
{ data: { source: '3', target: '10' } },
{ data: { source: '10', target: '11' } },
{ data: { source: '11', target: '12' } },
{ data: { source: '12', target: '13' } },
{ data: { source: '12', target: '14' } },
{ data: { source: '14', target: '15' } },
{ data: { source: '15', target: '16' } },
{ data: { source: '16', target: '17' } },
{ data: { source: '16', target: '18' } }
]
},
layout: {
name: "dagre",
rankDir: 'TB' //love this. you can quickly change the orientation here from LR(left to right) TB (top to bottom), RL, BT. Great dropdown option for users here.
},
style: [{
selector: 'node',
style: {
'label': 'data(id)',
'width': '30%',
'font-size': '20px',
'text-valign': 'center',
'shape': 'circle',
'background-color': 'rgba(113,158,252,1)',
'border': '2px grey #ccc'
}
}, {
selector: 'edge',
style: {
'width': 2,
'line-color': '#ccc',
'target-arrow-color': '#ccc',
'target-arrow-shape': 'triangle'
}
}]
});