如何在SVG线条上放置多个等距的箭头?

59

我刚接触SVG,正在尝试在两点之间绘制一条直线。目前我使用了以下命令:

<line x1="50" y1="50" x2="150" y2="150" style="stroke:rgb(255,255,0); stroke-width:2" stroke-dasharray="5,3" />"

最简单的方法是如何在这条线上添加小三角形或箭头(均匀分布),以指示方向?

编辑1:

为了更加清楚,我不是要在线末端添加箭头,而是在整个线段上均匀分布多个三角形。如果可能,我想用指向线的方向的三角形替换虚线中的每个破折号。

编辑2:

根据Phrogz的建议,我创建了一个页面,如下所示,但没有显示任何内容。我做错了什么?

<%@LANGUAGE="VBSCRIPT" CODEPAGE="65001"%>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
<title>Untitled Document</title>
<link href="css/com.css" rel="stylesheet" type="text/css" />
</head>
<body style="background:none;">
<svg xmlns="http://www.w3.org/2000/svg" viewBox="-10 -10 70 90">
<defs>
<marker id="t" markerWidth="4" markerHeight="4"
        orient="auto" refY="2">
  <path d="M0,0 L4,2 0,4" />
</marker>
</defs>
<polyline points="0,0 0,50 20,70 40,10 42,8 44,10, 46,14 50,50" />
</svg>
<script type="text/javascript">
midMarkers(document.querySelector('polyline'),6);

    // Given a polygon/polyline, create intermediary points along the
    // "straightaways" spaced no closer than `spacing` distance apart.
    // Intermediary points along each section are always evenly spaced.
    // Modifies the polygon/polyline in place.
    function midMarkers(poly,spacing){
        var svg = poly.ownerSVGElement;
        for (var pts=poly.points,i=1;i<pts.numberOfItems;++i){
            var p0=pts.getItem(i-1), p1=pts.getItem(i);
            var dx=p1.x-p0.x, dy=p1.y-p0.y;
            var d = Math.sqrt(dx*dx+dy*dy);
            var numPoints = Math.floor( d/spacing );
            dx /= numPoints;
            dy /= numPoints;
            for (var j=numPoints-1;j>0;--j){
                var pt = svg.createSVGPoint();
                pt.x = p0.x+dx*j;
                pt.y = p0.y+dy*j;
                pts.insertItemBefore(pt,i);
            }
            if (numPoints>0) i += numPoints-1;
        }
    }
</script>
</body>
</html>

5
需要注意的是,marker-mid 属性仅在你的路径 在起点和终点之间有顶点 时才有效。 - Édouard Lopez
2个回答

174
根据问题的澄清,这是一个创建沿着<polyline>元素中间点的实现,以便marker-mid="url(#arrowhead)"属性起作用。请参见下面的介绍,了解标记和箭头的相关信息。
演示:http://jsfiddle.net/Zv57N/
midMarkers(document.querySelector('polyline'),6);

// Given a polygon/polyline, create intermediary points along the
// "straightaways" spaced no closer than `spacing` distance apart.
// Intermediary points along each section are always evenly spaced.
// Modifies the polygon/polyline in place.
function midMarkers(poly,spacing){
  var svg = poly.ownerSVGElement;
  for (var pts=poly.points,i=1;i<pts.numberOfItems;++i){
    var p0=pts.getItem(i-1), p1=pts.getItem(i);
    var dx=p1.x-p0.x, dy=p1.y-p0.y;
    var d = Math.sqrt(dx*dx+dy*dy);
    var numPoints = Math.floor( d/spacing );
    dx /= numPoints;
    dy /= numPoints;
    for (var j=numPoints-1;j>0;--j){
      var pt = svg.createSVGPoint();
      pt.x = p0.x+dx*j;
      pt.y = p0.y+dy*j;
      pts.insertItemBefore(pt,i);
    }
    if (numPoints>0) i += numPoints-1;
  }
}

上述代码修改了现有的<polyline>元素,以在每条直线边缘上每spacing个单位添加点。将其与marker-mid结合使用,可以在每个顶点处放置一个旋转的标记,从而能够沿着路径一致地绘制任意复杂的形状/图形。

Bird Tracks

尽管代码在每个线段上均匀分布点(以避免在拐角处出现难看的“紧凑”情况),如上面的演示所示,但该代码不会移除路径中已经接近的点,这些点的距离小于间距值。

(以下是“标记入门”的原始答案。)


你想要定义一个SVG <marker> 元素,并在你的线条中添加marker-start="…"和/或marker-end="…"属性。使用标记将任意形状复制到路径的末端,并且(使用orient="auto")旋转该形状以匹配路径方向。
<svg xmlns="http://www.w3.org/2000/svg" viewBox="-50 -100 200 200">
  <defs>
    <marker id='head' orient='auto' markerWidth='2' markerHeight='4'
            refX='0.1' refY='2'>
      <path d='M0,0 V4 L2,2 Z' fill='red' />
    </marker>
  </defs>    
  <path
    marker-end='url(#head)'
    stroke-width='5' fill='none' stroke='black'  
    d='M0,0 C45,45 45,-45 90,0'
    />    
</svg>

示例:http://jsfiddle.net/Z5Qkf/1/

Curved line with arrowhead

在上面的内容中:
  • orient="auto"会使标记随着线条旋转
  • markerWidthmarkerHeight定义了一个类似于viewBox的边界框,用于标记。
    • 请注意,这些然后由最终线条的stroke-width进行缩放;高度为"4"会导致它在最终图形中宽度为20个单位(4×5)。
  • refXrefY定义了将形状放置在路径末端时的“原点”位置
    • 我使用了refX="0.1"来确保标记略微重叠于线条末端
  • 为了使自动定向正确工作,您希望标记的“正向”方向在+x方向上。(用于标记的红色路径在未旋转时看起来像这样▶。)
  • 您可以独立调整标记和/或线条的fill-opacitystroke-opacity,但是对线条opacity的更改将影响绘制的标记。
    • 由于线条和标记重叠,如果您降低标记的fill-opacity,您将看到重叠部分;但是,如果您降低线条本身的opacity,则标记会完全覆盖线条,并且两者的组合然后降低透明度。
      enter image description here

如果您希望在线条的长度上有箭头,您需要使用marker-mid="..."<path><polyline>以及沿线的中间点。
演示:http://jsfiddle.net/Z5Qkf/2/

enter image description here

唯一的问题是,沿着这条线路改变方向的任何点都会破坏方向;这就是为什么在演示中我使用了贝塞尔曲线来圆角,以便线上的中点位于直线段上。
<svg xmlns="http://www.w3.org/2000/svg" viewBox="-50 -100 200 200">
<defs>
  <marker id='mid' orient="auto"
    markerWidth='2' markerHeight='4'
    refX='0.1' refY='1'>
    <!-- triangle pointing right (+x) -->
    <path d='M0,0 V2 L1,1 Z' fill="orange"/>
  </marker>
  <marker id='head' orient="auto"
    markerWidth='2' markerHeight='4'
    refX='0.1' refY='2'>
    <!-- triangle pointing right (+x) -->
    <path d='M0,0 V4 L2,2 Z' fill="red"/>
  </marker>
</defs>

<path
  id='arrow-line'
  marker-mid='url(#mid)'
  marker-end='url(#head)'
  stroke-width='5'
  fill='none' stroke='black'  
  d='M0,0 L20,20 C40,40 40,40 60,20 L80,0'
  />

</svg>

要以程序化的方式实现这一点,您可以使用JavaScript和路径的getPointAtLength()命令来采样路径

1
感谢提供详细信息,但恐怕我的表述不太清楚。对此我深感抱歉。我想要的是在线的长度上有多个箭头。 - Osprey
1
@Osprey 哦,我明白了。看看我的编辑。不幸的是,答案并不像我们想象的那么简单或明确。 - Phrogz
1
@Osprey,我真是太傻了。或者:[].forEach.call(polys, function(p){ midMarkers(p,10) }); - Phrogz
我能找到的最好的关于标记方向、refXrefY的解释,谢谢。 - ptim
仅供参考,这是左箭头的标记符号<defs> <marker id='starthead' orient="auto" markerWidth='2' markerHeight='4' refX='2' refY='2'> <!-- 指向左侧的三角形(+x) --> <path d='M0,2 L2,0 V4 Z ' fill="red"/> </marker> </defs> - consoleart
显示剩余7条评论

11

以下是一些有用的链接和示例:

1. 箭头可以是二次曲线 enter image description here

2. 立方曲线 enter image description here

文档:https://developer.mozilla.org/en-US/docs/Web/SVG/Tutorial/Paths

演示: 这里实现了两种箭头:

<!DOCTYPE html>
<html>
<head>
  <meta charset="utf-8">
  <meta name="viewport" content="width=device-width">
  <title>JS Bin</title>
</head>
<body>

  
  <svg xmlns="http://www.w3.org/2000/svg" viewBox="-50 -100 200 200">
<defs>
  <marker id='head' orient="auto"
    markerWidth='2' markerHeight='4'
    refX='0.1' refY='2'>
    <!-- triangle pointing right (+x) -->
    <path d='M0,0 V4 L2,2 Z' fill="black"/>
  </marker>
</defs>

<path
  id='arrow-line'
  marker-end='url(#head)'
  stroke-width='1'
  fill='none' stroke='black'  
  d='M0,0 Q45,-20 90,0'
  />
    
<path
  id='arrow-line'
  marker-end='url(#head)'
  stroke-width='1'
  fill='none' stroke='black'  
  d='M0,50 C10,30 80,30 90,50'
  />

</svg>
</body>
</html>


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