19

我正在使用 D3 绘制一个力导向图,这与此示例非常相似:http: //bl.ocks.org/mbostock/1153292

我试图将箭头放在链接的中间而不是末端。

使用attr("refX", 0)标记并没有多大帮助,因为它是绝对的,与链接长度无关——我的链接长度不同。

我一直在谷歌搜索,我最好的想法是根据这个例子link.attr("marker-end", ...)替换(寻找图表中间的十字架)。但这似乎不起作用..我猜是因为它只是 SVG2 草案的一部分,但我的浏览器支持较低版本?(我正在使用最新版本的 Chrome 顺便说一句)。link.attr("marker-segment", ...)

如何将箭头放在链接的中间?

4

3 回答 3

19

不要将标记放在末端,而是在线条中间创建一个点(<polyline>或),并使用它来应用箭头。<path>marker-mid

我的这个答案的这个演示使用这种技术沿着路径的长度创建多个内部点marker-mid。您只需创建一个“航点”而不是多个。

编辑:这是对现有演示的简单破解,显示了我的意思:

演示:http: //jsfiddle.net/tk7Wv/2/

曲线中间的箭头

唯一的变化是:

  1. 更改marker-endmarker-mid, 和
  2. 修改路径生成代码以创建两条弧线(在中间连接)而不是一条。

它需要一些简单的三角函数,如 Superboggly 所示,根据你想要的线条弯曲量来选择合适的中点。

于 2013-04-01T18:47:22.537 回答
17

我是 OP,为了其他有相同问题的人,这个答案只是对上述优秀答案的补充。

答案显示了如何为带有弧链接的图形实现这一点。如果您有直接链接,则需要稍微修改接受的答案。这是如何:

您的直接链接可能是用 实现的line,它们需要转换为polyline. 像这样:

// old: svg.selectAll(".link").data(links).enter().append("line")
svg.selectAll(".link").data(links).enter().append("polyline")

然后我们必须根据这个例子对折线进行编码,所以你的原始代码编码line

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;});

更改为:

force.on("tick", function() {
   link.attr("points", function(d) {
      return d.source.x + "," + d.source.y + " " + 
             (d.source.x + d.target.x)/2 + "," + (d.source.y + d.target.y)/2 + " " +
             d.target.x + "," + d.target.y; });

最后,不要忘记转换marker-endmarker-mid

// old: link.attr("marker-end",
link.attr("marker-mid",

感谢@Phrogz 为您指明方向。

于 2013-04-02T07:47:36.527 回答
10

我采取了与 Phrogz 略有不同的方法。我尝试删除路径的标记,而是使用一条新路径绘制它,该路径到达弧的中点并且其笔划是不可见的。计算中点有点挑剔,所以如果你想改变弧的特征,你可能会更好地使用 Phrogz 的方法,或者一些混合方法。

在这种情况下,trig 并不是那么糟糕,因为如果您仔细观察,您会注意到用于生成链接的弧来自具有相同半径和节点之间距离的圆。所以你有一个等边三角形,你真正需要做的就是计算从连接线段的中点到圆弧中点的距离。

我想我需要一个图表来解释:

找到圆弧中点 所以三角形ABC是等边的,EC平分AB。因为我们有 A 和 B 的坐标,所以很容易找到 M(平均坐标)。这意味着我们也知道从 A 到 B 的斜率(delta y / delta x),所以从 M 到 E 的斜率是负数。知道 M 和 E 的斜率意味着我们快到了,我们只需要知道要走多远。

为此,我们使用 ACM 是 30-60-90 三角形的事实,因此

|厘米| = sqrt(3) * |上午|

但是|上午| = |AB| / 2 和 |CE| = |AB|

所以

|我| = |AB| - sqrt(3) * |AB| / 2

为了使这个长度有意义,我们必须用斜率向量的长度对其进行归一化,我们已经知道它与圆的半径相同。

无论如何,把它们放在一起你会得到:

var markerPath = svg.append("svg:g").selectAll("path.marker")
  .data(force.links())
  .enter().append("svg:path")
  .attr("class", function(d) { return "marker_only " + d.type; })
  .attr("marker-end", function(d) { return "url(#" + d.type + ")"; });


... later in the tick handler

markerPath.attr("d", function(d) {
  var dx = d.target.x - d.source.x,
      dy = d.target.y - d.source.y,
      dr = Math.sqrt(dx * dx + dy * dy);

  // We know the center of the arc will be some distance perpendicular from the
  // link segment's midpoint. The midpoint is computed as:
  var endX = (d.target.x + d.source.x) / 2;
  var endY = (d.target.y + d.source.y) / 2;

  // Notice that the paths are the arcs generated by a circle whose 
  // radius is the same as the distance between the nodes. This simplifies the 
  // trig as we can simply apply the 30-60-90 triangle rule to find the difference
  // between the radius and the distance to the segment midpoint from the circle 
  // center.
  var len = dr - ((dr/2) * Math.sqrt(3));

  // Remember that is we have a line's slope then the perpendicular slope is the 
  // negative inverse.
  endX = endX + (dy * len/dr);
  endY = endY + (-dx * len/dr);

  return "M" + d.source.x + "," + d.source.y + "A" + dr + "," + dr + " 0 0,1 " + endX + "," + endY;
});

在这里查看。请注意,我的标记路径的路径 css 设置为透明红色,通常您希望将 stroke 设置为“none”以隐藏线条。

于 2013-04-01T22:36:15.417 回答