简体   繁体   English

在 D3 树布局中使用多种链接类型

[英]Using Multiple Link Types in D3 Tree Layout

I'm using Tree Layout to construct a complex diagram in D3 v3.我正在使用 Tree Layout 在 D3 v3 中构建一个复杂的图表。 My diagram normally has a tree structure, so I'm using the diagonal links as follows:我的图表通常具有树结构,因此我使用对角线链接如下:

var diagonal = d3.svg.diagonal()
  .projection(function (d) {
    return [d.x, d.y];
  });

However, for some of the nodes in my diagram, I have a different type of nodes which show the attributes of their parent nodes.但是,对于图表中的某些节点,我有不同类型的节点来显示其父节点的属性。 Therefore, I want them to be very close to their parent nodes rather than in the same level with the other children nodes.因此,我希望它们与父节点非常接近,而不是与其他子节点处于同一级别。 I thought a possible solution could be using radial links as follows:我认为一个可能的解决方案可能是使用如下径向链接:

var radialDiagonal = d3.svg.diagonal.radial()
  .projection(function (d) {
    return [d.y, d.x / 180 * Math.PI];
  });

But, I couldn't find any example on how to use multiple link types in a diagram.但是,我找不到任何关于如何在图表中使用多种链接类型的示例。 Is it possible to do this?是否有可能做到这一点? Or, is there any solution to make some nodes so close to their parent nodes?或者,是否有任何解决方案可以使某些节点与其父节点如此接近?

Thanks in advance.提前致谢。

I'm using d3v4+ syntax for a tree, the approaches below are adoptable to v3 relatively easily我正在为树使用 d3v4+ 语法,下面的方法相对容易地适用于 v3

It does not sound like the ability to use different types of links is what you are looking for.听起来您正在寻找使用不同类型链接的能力。

The link type, d3.svg.diaganol here is irrelevant to node placement.此处的链接类型d3.svg.diaganol与节点放置无关。 The link only connects two points with some sort of pre-defined curve.该链接仅使用某种预定义的曲线连接两个点。 So if you want to allow nodes "to be very close to their parent nodes rather than in the same level with the other children nodes", you want to manipulate the location of the nodes, not the actual links.因此,如果您想允许节点“非常接近其父节点,而不是与其他子节点处于同一级别”,您需要操纵节点的位置,而不是实际的链接。 One option would be to manually move nodes that should be close to their parent node.一种选择是手动移动应该靠近其父节点的节点。 In the example below I use a node property ( near ) to filter through nodes (after running the layout generator) and move those that should be closer to their parent:在下面的示例中,我使用节点属性 ( near ) 过滤节点(在运行布局生成器之后)并移动那些应该更靠近其父节点的节点:

 root.descendants().reverse().forEach(function(d) {
  if(d.data.near) d.y = (d.parent.y+d.y)/2;
 })

 var data = { "name": "Parent", "children": [ { "name": "Child A", near:true, "children": [ { "name": "Grandkid A", near: true }, { "name": "Grandkid B" }, { "name": "Grandkid C" } ] }, { "name": "Child B", }, { "name": "Child C", children: [ { "name": "Grandkid D", near:true }, { "name": "Grandkid E" } ]} ] }; var width = 400; var height = 300; margin = {left: 50, top: 10, right:50, bottom: 10} var svg = d3.select("body").append("svg").attr("width", width).attr("height", height); var g = svg.append("g").attr('transform','translate('+ margin.left +','+ margin.right +')'); var root = d3.hierarchy(data); var tree = d3.tree().size([height-margin.top-margin.bottom,width-margin.left-margin.right]); tree(root) root.descendants().reverse().forEach(function(d) { if(d.data.near) dy = (d.parent.y+dy)/2; }) var link = g.selectAll(".link").data(root.links()).enter().append("path").attr("class", "link").attr("d", d3.linkHorizontal().x(function(d) { return dy; }).y(function(d) { return dx; })); var node = g.selectAll(".node").data(root.descendants()).enter().append("g").attr("class", function(d) { return "node" + (d.children? " node--internal": " node--leaf"); }).attr("transform", function(d) { return "translate(" + dy + "," + dx + ")"; }) node.append("circle").attr("r", 2.5); node.append("text").text(function(d) { return d.data.name; }).attr('y',-10).attr('x',-10).attr('text-anchor','middle');
 path{ fill: #fff; stroke: steelblue; stroke-width: 3px; }.link { fill: none; stroke: #ccc; stroke-width: 2px; }
 <script src="https://cdnjs.cloudflare.com/ajax/libs/d3/5.7.0/d3.min.js"></script>

The above solution is pretty basic, in a complicated tree with a lots of depth, it might get a lot more difficult.上述解决方案非常基本,在具有很多深度的复杂树中,它可能会变得更加困难。 You could also manually place the x of every node, which offers more control, but comes at the expense of writing a new alorithm to replace or correct at least half of the tree layout generator output.您还可以手动放置每个节点的 x,这提供了更多控制,但代价是编写一个新的算法来替换或纠正至少一半的树布局生成器 output。 Ultimately the "right" answer will depend on preference and data structure.最终,“正确”的答案将取决于偏好和数据结构。

If you want to use a different link generator, this is also easy.如果您想使用不同的链接生成器,这也很容易。 I'll use a node property ( near again) to indicate if an alternative link should be used:我将使用节点属性(再次near )来指示是否应使用替代链接:

 var data = { "name": "Parent", "children": [ { "name": "Child A", near:true, "children": [ { "name": "Grandkid A", near: true }, { "name": "Grandkid B" }, { "name": "Grandkid C" } ] }, { "name": "Child B", }, { "name": "Child C", children: [ { "name": "Grandkid D", near: true}, { "name": "Grandkid E" } ]} ] }; var width = 400; var height = 300; margin = {left: 50, top: 10, right:50, bottom: 10} var svg = d3.select("body").append("svg").attr("width", width).attr("height", height); var g = svg.append("g").attr('transform','translate('+ margin.left +','+ margin.right +')'); var root = d3.hierarchy(data); var tree = d3.tree().size([height-margin.top-margin.bottom,width-margin.left-margin.right]); tree(root) root.descendants().forEach(function(d) { if(d.data.near) dy = (d.parent.y+dy)/2; }) var horizontal = d3.linkHorizontal().x(function(d) { return dy; }).y(function(d) { return dx; }) // some different path drawing function: var straight = function(d) { return "M"+d.source.y+" "+d.source.x+"L"+(d.target.y-20)+" "+d.source.x+"L"+d.target.y+" "+d.target.x; } var link = g.selectAll(".link").data(root.links()).enter().append("path").attr("class", "link").attr("d", function(d) { if(.d.target.data;near) return straight(d); else return horizontal(d); }). var node = g.selectAll(".node").data(root.descendants()).enter().append("g"),attr("class". function(d) { return "node" + (d?children: " node--internal"; " node--leaf"). }),attr("transform". function(d) { return "translate(" + d,y + "." + d;x + ")". }) node.append("circle"),attr("r". 2;5). node.append("text").text(function(d) { return d.data;name. }),attr('y'.-10),attr('x'.-10),attr('text-anchor';'middle');
 path{ fill: #fff; stroke: steelblue; stroke-width: 3px; }.link { fill: none; stroke: #ccc; stroke-width: 2px; }
 <script src="https://cdnjs.cloudflare.com/ajax/libs/d3/5.7.0/d3.min.js"></script>

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM