javascript 在 D3 力布局链接中间显示一个箭头
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/15729856/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
Display an arrow head in the middle of D3 force layout link
提问by talkol
I'm using D3 to draw a force-directed graph, which is very similar to this example: http://bl.ocks.org/mbostock/1153292
我正在使用 D3 绘制一个力有向图,这与此示例非常相似:http: //bl.ocks.org/mbostock/1153292
I'm trying to place the arrowheads in the middle of the links instead of the end.
我试图将箭头放在链接的中间而不是末端。
Playing with the attr("refX", 0)
of the marker doesn't help much, because it's absolute and not relative to the link length - my links have varying lengths.
使用attr("refX", 0)
标记的 并没有多大帮助,因为它是绝对的而不是相对于链接长度的 - 我的链接有不同的长度。
I've been googling around, and my best idea was to replace link.attr("marker-end", ...)
with link.attr("marker-segment", ...)
according to this example(look for the crosses in the middle of the graphs). But this doesn't seem to work.. I'm guessing because it's part of SVG2 draft only but my browser supports a lower version? (I'm using the most recent ver of Chrome btw).
我一直在谷歌上搜索周围,和我最好的想法是,以取代link.attr("marker-end", ...)
与link.attr("marker-segment", ...)
本例(在图中间找交叉盘)。但这似乎不起作用..我猜是因为它只是 SVG2 草案的一部分,但我的浏览器支持较低版本?(顺便说一句,我使用的是最新版本的 Chrome)。
How can I place the arrowheads in the middle of the links?
如何将箭头放在链接的中间?
回答by Phrogz
Instead of placing the marker on an end, create your lines (<polyline>
or <path>
) with a single point in the middle of the line and use marker-mid
to apply the arrowheads.
不要将标记放在末端,而是在线条中间创建一个点(<polyline>
或<path>
)并用于marker-mid
应用箭头。
This demofor this answerof mine uses this technique to create multiple interior points along the length of a path along with marker-mid
. You would simply be creating a single 'waypoint' instead of many.
此演示了这个答案我的使用这个技术一起沿着路径长度创建多个内部点marker-mid
。您只需创建一个“航点”而不是多个“航点”。
Edit: Here's a simple hack to the existing demo showing what I mean:
编辑:这是对现有演示的简单破解,显示了我的意思:
Demo: http://jsfiddle.net/tk7Wv/2/
演示:http: //jsfiddle.net/tk7Wv/2/
The only changes are:
唯一的变化是:
- Changing
marker-end
tomarker-mid
, and - Modifying the path generation code to create two arcs (connected in the middle) instead of one.
- 更改
marker-end
为marker-mid
, 和 - 修改路径生成代码以创建两个圆弧(在中间连接)而不是一个。
It will require some simple trigonometry like Superboggly illustrates to pick an appropriate midpoint based on the amount of bowing you want in your lines.
它需要一些简单的三角学,如 Superboggly 说明,以根据您想要的线条弯曲量选择合适的中点。
回答by talkol
I'm the OP, this answer is just an addition to the excellent answers above for the sake of others with the same question.
我是 OP,这个答案只是为了其他有相同问题的人对上述优秀答案的补充。
The answers show how to achieve this for a graph with arc links. If you have straight links, the accepted answer needs to be modified slightly. This is how:
答案显示了如何为带有弧形链接的图形实现这一点。如果您有直接链接,则需要稍微修改已接受的答案。这是如何:
Your straight links are probably implemented with line
, they need to be converted into polyline
. Like so:
您的直接链接可能是用 实现的line
,它们需要转换为polyline
. 像这样:
// old: svg.selectAll(".link").data(links).enter().append("line")
svg.selectAll(".link").data(links).enter().append("polyline")
Then we have to encode the polyline according to this example, so your original code that encodes the line
:
然后我们必须根据此示例对折线进行编码,因此您对以下内容进行编码的原始代码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;});
Changes into:
更改为:
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; });
And last, don't forget the convert marker-end
to marker-mid
:
最后,不要忘记转换marker-end
为marker-mid
:
// old: link.attr("marker-end",
link.attr("marker-mid",
Credits to @Phrogz for showing the way.
感谢@Phrogz 为我们指明了方向。
回答by Superboggly
I took a slightly different approach from Phrogz. I tried removing the path's marker and instead draw it using a new path that goes to the arc's midpoint and whose stroke is invisible. Computing the midpoint is a little finicky so if you want to change the characteristics of the arc you might be better served with Phrogz's approach, or some hybrid.
我采取了与 Phrogz 略有不同的方法。我尝试删除路径的标记,而是使用一条到达圆弧中点且笔触不可见的新路径来绘制它。计算中点有点挑剔,所以如果你想改变弧线的特征,你可能会更好地使用 Phrogz 的方法,或者一些混合方法。
In this case the trig is not so bad because if you look closely you will notice the arc used to generate the links are from a circle with the same radius and the distance between the nodes. So you have an equilateral triangle and all you need to do really is compute the distance passed the midpoint of the link line segment to the arc midpoint.
在这种情况下,trig 还不错,因为如果仔细观察,您会注意到用于生成链接的弧来自具有相同半径和节点之间距离的圆。所以你有一个等边三角形,你真正需要做的就是计算从连接线段的中点到圆弧中点的距离。
I think I need a diagram to explain:
我想我需要一张图来解释:
So triangle ABC is equilateral and EC bisect AB. Since we have coordinates for A and B finding M is easy (average the coordinates). It means we also know the slope from A to B (delta y / delta x) so the slope from M to E is the negative inverse. Knowing M and the slope to E means we are almost there, we just need to know how far to go.
所以三角形ABC是等边的,EC平分AB。由于我们有 A 和 B 的坐标,找到 M 很容易(平均坐标)。这意味着我们也知道从 A 到 B 的斜率(delta y / delta x),所以从 M 到 E 的斜率是负的倒数。知道 M 和 E 的斜率意味着我们快到了,我们只需要知道要走多远。
To do that we use the fact that ACM is a 30-60-90 triangle and so
为此,我们使用 ACM 是一个 30-60-90 三角形的事实,因此
|CM| = sqrt(3) * |AM|
|厘米| = sqrt(3) * |上午|
but |AM| = |AB| / 2 and |CE| = |AB|
但是|上午| = |AB| / 2 和 |CE| = |AB|
So
所以
|ME| = |AB| - sqrt(3) * |AB| / 2
|我| = |AB| - sqrt(3) * |AB| / 2
In order for this length to make sense we will have to normalize it with the length of the slope vector, which we already know is the same as the circle's radius.
为了使这个长度有意义,我们必须用斜率向量的长度对其进行归一化,我们已经知道它与圆的半径相同。
Anyway, putting it all together you get:
无论如何,把它们放在一起你会得到:
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;
});
Check it out here. Note that my path css for the marker path's is set to a transparent red, normally you would want to set stroke to 'none' to hide the lines.
检查它在这里。请注意,我的标记路径的路径 css 设置为透明红色,通常您希望将笔触设置为“无”以隐藏线条。