在D3力布局链接中间显示箭头

tal*_*kol 16 javascript svg d3.js force-layout

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

我试图将箭头放在链接的中间而不是结尾.

使用attr("refX", 0)标记的帮助并不多,因为它是绝对的而不是相对于链接长度 - 我的链接有不同的长度.

我一直在谷歌上搜索周围,和我最好的想法是,以取代link.attr("marker-end", ...)link.attr("marker-segment", ...)示例(查找在图中间的十字).但这似乎不起作用..我猜是因为它只是SVG2草案的一部分,但我的浏览器支持较低版本?(我正在使用最新版本的Chrome btw).

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

Phr*_*ogz 19

不要将标记放在一端,而是在线条中间创建一条带有单个点的线条(<polyline><path>),并用于marker-mid应用箭头.

这个答案演示使用这种技术沿着路径的长度创建多个内部点marker-mid.你只需创建一个"航点"而不是许多.

编辑:这是对现有演示的一个简单的黑客,显示我的意思:

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

箭头中间的箭头

唯一的变化是:

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

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


tal*_*kol 14

我是OP,这个答案只是上面的优秀答案的补充,为了其他人有同样的问题.

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

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

// old: svg.selectAll(".link").data(links).enter().append("line")
svg.selectAll(".link").data(links).enter().append("polyline")
Run Code Online (Sandbox Code Playgroud)

然后我们必须根据这个例子对折线进行编码,所以你的原始代码编码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;});
Run Code Online (Sandbox Code Playgroud)

变成:

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; });
Run Code Online (Sandbox Code Playgroud)

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

// old: link.attr("marker-end",
link.attr("marker-mid",
Run Code Online (Sandbox Code Playgroud)

归功于@Phrogz展示方式.


Sup*_*gly 10

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

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

我想我需要一个图解释:

找到弧中点 因此,三角形ABC是等边的,EC等于AB.由于我们有A和B的坐标,因此发现M很容易(平均坐标).这意味着我们也知道从A到B的斜率(delta y/delta x),因此从M到E的斜率是负反.知道M和E的斜率意味着我们几乎就在那里,我们只需知道要走多远.

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

| CM | = sqrt(3)*| AM |

但是| AM | = | AB |/2和| CE | = | AB |

所以

| ME | = | 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;
});
Run Code Online (Sandbox Code Playgroud)

检查它在这里.请注意,标记路径的路径css设置为透明红色,通常您需要将笔划设置为"无"以隐藏线条.