d3.svg.diagonal()在哪里?

53

我想尝试执行可折叠树的代码,就像这里所提到的那样。但是在v4中似乎对角线方法不适用(我可能是错的)。

关于:

var diagonal = d3.svg.diagonal()

我遇到了这个错误:

TypeError: 无法读取未定义的属性“diagonal”

在v4中相当于什么?查看d3.js API文档没有给我任何线索。

5个回答

58

D3 4.9.0引入link shapes,其功能与D3 v3中的d3.svg.diagonal相同。根据API,链接形状从源点到目标点生成平滑的三次贝塞尔曲线。曲线在起点和终点处的切线可以是垂直的、水平的或径向的。有三种方法:

因此,对于像您链接的可折叠树形图,可以将路径d属性定义为:

.attr("d", d3.linkHorizontal()
    .x(function(d) { return d.y; })
    .y(function(d) { return d.x; }));

演示:

假设您有一个对象,其中包含 sourcetarget,每个对象都有 xy 属性:

var data = {
  source: {
    x: 20,
    y: 10
  },
  target: {
    x: 280,
    y: 100
  }
};

首先,您需要创建链接生成器:

var link = d3.linkHorizontal()
  .x(function(d) {
    return d.x;
  })
  .y(function(d) {
    return d.y;
  });

然后,您只需将该数据传递给链接生成器即可绘制路径:

.attr("d", link(data))

这是演示:

var svg = d3.select("svg");

var data = {
  source: {
    x: 20,
    y: 10
  },
  target: {
    x: 280,
    y: 100
  }
};

var link = d3.linkHorizontal()
  .x(function(d) {
    return d.x;
  })
  .y(function(d) {
    return d.y;
  });

svg.append("path")
  .attr("d", link(data))
  .style("fill", "none")
  .style("stroke", "darkslateblue")
  .style("stroke-width", "4px");
<script src="https://d3js.org/d3.v5.min.js"></script>
<svg></svg>


1
你如何导入(es6)d3.linkHorizontal()这部分? - erp
1
@erp 导入 { linkHorizontal } 自 'd3-shape' - iou90
看起来有人在他们的网站上镜像了你的答案,链接在这里:https://www.tutorialguruji.com/javascript/where-is-d3-svg-diagonal/ - ADJenks

22

请查看 GitHub 问题链接

虽然该问题仍未解决,但似乎 Bostock 先生不着急在版本4中重新实现它。为什么?因为你自己也可以轻松地实现:

function link(d) {
  return "M" + d.source.y + "," + d.source.x
      + "C" + (d.source.y + d.target.y) / 2 + "," + d.source.x
      + " " + (d.source.y + d.target.y) / 2 + "," + d.target.x
      + " " + d.target.y + "," + d.target.x;
}

3
就像你上面的例子一样,我见过很多情况下 'y' 被用来代替 'x',反之亦然。这是什么原因? - Mopparthy Ravindranath
3
@MupparthyRavindranath,水平与垂直绘制树形图。垂直绘制会颠倒x和y的位置,就像这个例子一样。 - Mark

17

我一开始遇到这个问题时很难解决,但几个小时后,我意识到它实际上非常简单(就像其他人提到的那样)。 请更换:

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

...用这个:

var diagonal = function link(d) {
  return "M" + d.source.y + "," + d.source.x
      + "C" + (d.source.y + d.target.y) / 2 + "," + d.source.x
      + " " + (d.source.y + d.target.y) / 2 + "," + d.target.x
      + " " + d.target.y + "," + d.target.x;
};

那应该是唯一的变化。希望这可以帮助其他人。这应该适用于像Patrick Brockman的可折叠/可搜索树这样的可视化。


2
这与被接受的答案有何不同? - altocumulus
1
抱歉,我只是想添加额外的澄清。 - Pete Adam Bialecki
2
我认为这个答案更清晰,因为它明确提到了哪一部分应该被替换成哪一部分。这正是人们所寻找的。在其他答案中,并没有明确说明要替换什么。 - exchange

0

对于那些有垂直树的人,下面的函数将产生弯曲的对角线,如this example所示。

这个函数是通过以下两种方式修改Mark的答案而得到的:(1)交换x和y的调用,(2)在两行中间的计算坐标。如果没有坐标变化,曲线会被倒置,例如this post

var diagonal = function link(d) {
  return "M" + d.source.x + "," + d.source.y
      + "C" + d.source.x + "," + (d.source.y + d.target.y) / 2
      + " " + d.target.x + "," + (d.source.y + d.target.y) / 2
      + " " + d.target.x + "," + d.target.y;
};

作为一个侧注,如果你想要方形的边缘而不是圆形的边缘,你可以用空格" "替换"C"

-3

我和你遇到了同样的问题,但是!D3的V6版本有一个新的树形结构可用(基于Mike Bostock的工作): https://bl.ocks.org/d3noob/9de0768412ac2ce5dbec430bb1370efe

我把下面的代码复制在这里,以防万一链接因为某些未知原因而消失:

.node circle { fill: #fff; stroke: steelblue; stroke-width: 3px; } .node text { font: 12px sans-serif; } .link { fill: none; stroke: #ccc; stroke-width: 2px; }
var treeData = { "name": "顶层", "children": [ { "name": "第二层:A", "children": [ { "name": "A的儿子" }, { "name": "A的女儿" } ] }, { "name": "第二层:B" } ] };
var margin = {top: 20, right: 90, bottom: 30, left: 90}, width = 960 - margin.left - margin.right, height = 500 - margin.top - margin.bottom;
var svg = d3.select("body").append("svg") .attr("width", width + margin.right + margin.left) .attr("height", height + margin.top + margin.bottom) .append("g") .attr("transform", "translate(" + margin.left + "," + margin.top + ")");
var i = 0, duration = 750, root;
var treemap = d3.tree().size([height, width]);
root = d3.hierarchy(treeData, function(d) { return d.children; }); root.x0 = height / 2; root.y0 = 0;
root.children.forEach(collapse); update(root);
function collapse(d) { if(d.children) { d._children = d.children d._children.forEach(collapse) d.children = null } }
function update(source) {
var treeData = treemap(root);
var nodes = treeData.descendants(), links = treeData.descendants().slice(1);
nodes.forEach(function(d){ d.y = d.depth * 180});
var node = svg.selectAll('g.node') .data(nodes, function(d) {return d.id || (d.id = ++i); });
var nodeEnter = node.enter().append('g') .attr('class', 'node') .attr("transform", function(d) { return "translate(" + source.y0 + "," + source.x0 + ")"; }) .on('click', click);
nodeEnter.append('circle') .attr('class', 'node') .attr('r', 1e-6) .style("fill", function(d) { return d._children ? "lightsteelblue" : "#fff"; });
nodeEnter.append('text') .attr("dy", ".35em") .attr("x", function(d) { return d.children || d._children ? -13 : 13; }) .attr("text-anchor", function(d) { return d.children || d._children ? "end" : "start"; }) .text(function(d) { return d.data.name; });
var nodeUpdate = nodeEnter.merge(node);
nodeUpdate.transition() .duration(duration) .attr("transform", function(d) { return "translate(" + d.y + "," + d.x + ")"; });
nodeUpdate.select('circle.node') .attr('r', 10) .style("fill", function(d) { return d._children ? "lightsteelblue" : "#fff"; }) .attr('cursor', 'pointer');
var nodeExit = node.exit().transition() .duration(duration) .attr("transform", function(d) { return "translate(" + source.y + "," + source.x + ")"; }) .remove();
nodeExit.select('circle') .attr('r', 1e-6);
nodeExit.select('text') .style('fill-opacity', 1e-6);
var link = svg.selectAll('path.link') .data(links, function(d) { return d.id; });
var linkEnter = link.enter().insert('path', "g") .attr("class", "link") .attr('d', function(d){ var o = {x: source.x0, y: source.y0} return diagonal(o, o) });
var linkUpdate = linkEnter.merge(link); linkUpdate.transition() .duration(duration) .attr('d', function(d){ return diagonal(d, d.parent) });
var linkExit = link.exit().transition() .duration(duration) .attr('d', function(d) { var o = {x: source.x, y: source.y} return diagonal(o, o) }) .remove();
nodes.forEach(function(d){ d.x0 = d.x; d.y0 = d.y; });
function diagonal(s, d) { path = `M ${s.y} ${s.x} C ${(s.y + d.y) / 2} ${s.x}, ${(s.y + d.y) / 2} ${d.x}, ${d.y} ${d.x}` return path }
function click(event, d) { if (d.children) { d._children = d.children; d.children = null; } else { d.children = d

您还会找到一些其他示例 在那里


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接