如何在两个圆圈之间绘制一条箭头线,给出:
我正在使用line和marker svg对象.
如果我将箭头绘制到圆圈的"中心" - 那么箭头是不可见的.如果我将箭头向后移动太远 - 那么该线显示并隐藏箭头的尖端(这里为了更好的可见性而夸大):

根据请求,这是我的代码的相关位(在生活脚本中):
# Draw an arrow to use for lines
svg.append("svg:defs")
.append("svg:marker")
.attr("id", "arrow")
.attr("viewBox", "0 0 10 10")
.attr("refX", 27)
.attr("refY", 5)
.attr("markerUnits", "strokeWidth")
.attr("markerWidth", 8)
.attr("markerHeight", 6)
.attr("orient", "auto")
.append("svg:path")
.attr("d", "M 0 0 L 10 5 L 0 10 z")
svg.append("line")
.attr "x1" 5
.attr "x2" 50
.attr "y1" 5
.attr "y2" 50
.style "stroke" "black"
.attr "stroke-width" 2
.attr "marker-end" "url(\#arrow)"
Run Code Online (Sandbox Code Playgroud)
或者,这里是工作示例的JSFiddle(注意箭头是"坐立不安"看起来恰到好处):http://jsfiddle.net/yeQS2/
Joa*_*ant 15
如果我理解正确,您需要找到需要添加到源的2D矢量才能到达目标圆的边界.
伪代码:
d = distance between A and B; // (sqrt((xB-xA)² + (yB-yA)²)).
d2 = d - radius;
ratio = d2 / d;
dx = (xB - xA) * ratio;
dy = (yB - yA) * ratio;
x = xA + dx;
y = yA + dy;
Run Code Online (Sandbox Code Playgroud)
我有同样的问题,这就是我如何解决它.对原始小提琴的改动:
更改.attr("refX", 27)到.attr("refX", 0).这使得箭头绘制超出了线的末端.
使用三角函数计算线的正确结束位置,考虑箭头,通过将以下代码添加到"tick":
var arrowheadLength = 8, // from markerWidth
nodeRadius = 10;
link.each(function(d) {
var x1 = d.source.x,
y1 = d.source.y,
x2 = d.target.x,
y2 = d.target.y,
angle = Math.atan2(y2 - y1, x2 - x1);
d.targetX = x2 - Math.cos(angle) * (nodeRadius + arrowheadLength);
d.targetY = y2 - Math.sin(angle) * (nodeRadius + arrowheadLength);
});
Run Code Online (Sandbox Code Playgroud)
使用计算的targetX和targetY链接属性:
.attr("x2", function(d){
return d.targetX;
}).attr("y2", function(d){
return d.targetY;
})
Run Code Online (Sandbox Code Playgroud)
这是更新的小提琴.
好的,所以我想尝试一下并用一些向量数学来实现它,它更漂亮并且结果是可重用的。
一些澄清:
假设我们希望它动态工作(“每个刻度”),初始链接调整如下所示(我正在使用coffeescript):
links.attr('x1', ({source,target}) -> source.x)
.attr('y1', ({source,target}) -> source.y)
.attr('x2', ({source,target}) -> target.x)
.attr('y2', ({source,target}) -> target.y)
Run Code Online (Sandbox Code Playgroud)
我们想要做的是将源和目标移nodeRadius离圆。为此,我们使用向量数学来
nodeRadius。这个新向量表示节点中心与其边界之间的距离,其方向与链接相同。好的,我们将使用以下函数来做到这一点:
length = ({x,y}) -> Math.sqrt(x*x + y*y)
sum = ({x:x1,y:y1}, {x:x2,y:y2}) -> {x:x1+x2, y:y1+y2}
diff = ({x:x1,y:y1}, {x:x2,y:y2}) -> {x:x1-x2, y:y1-y2}
prod = ({x,y}, scalar) -> {x:x*scalar, y:y*scalar}
div = ({x,y}, scalar) -> {x:x/scalar, y:y/scalar}
unit = (vector) -> div(vector, length(vector))
scale = (vector, scalar) -> prod(unit(vector), scalar)
free = ([coord1, coord2]) -> diff(coord2, coord1)
Run Code Online (Sandbox Code Playgroud)
这可能看起来有点不知所措,它非常紧凑,因为coffeescript允许我们直接在方法签名中解构事物,非常方便!正如您所看到的,还有另一个函数称为scale. 这只是一个组合步骤 2. 和 3 的便利函数。
现在让我们尝试为链接源设置新的 x 坐标。请记住:坐标应移动nodeRadius,以便它从圆的边界开始,而不是在圆内部。
(d) ->
# Step 1
freed = free(d)
# Step 2
unit = unit(freed)
# Step 3
scaled = prod(unit, nodeRadius)
# Step 2+3 would be scale(freed, nodeRadius)
# Step 4, coords are pretty much just vectors,
# so we just use the sum() function to move the source coords
coords = sum(d.source, scaled)
return coords.x
Run Code Online (Sandbox Code Playgroud)
没什么可说的!将所有这些放入tick()函数中,我们得到:
links.attr('x1', ({source,target}) -> sum(source, scale(free([source,target]), nodeRadius)).x)
.attr('y1', ({source,target}) -> sum(source, scale(free([source,target]), nodeRadius)).y)
.attr('x2', ({source,target}) -> diff(target, scale(free([source,target]), nodeRadius)).x)
.attr('y2', ({source,target}) -> diff(target, scale(free([source,target]), nodeRadius)).y)
Run Code Online (Sandbox Code Playgroud)
哦,不要忘记从目标坐标中减去,否则你只会再次使线更长(即移动它nodeRadius)。