【问题标题】:Block arrow using SVG使用 SVG 的块箭头
【发布时间】:2017-06-03 21:32:26
【问题描述】:

我需要使用 SVG 从一个点 (x0,y0) 到另一个点 (x1,y1) 绘制漂亮的描边箭头,就像图片上的那个。

我能想象的唯一方法是使用带有标记的线(两条线基本上用于模拟笔触和填充),但由于笔触重叠,它看起来有点难看。

理想情况下,线条和标记都应该用相同的颜色填充,并且应该具有相同的笔划颜色,并且整体箭头宽度可以固定(但如果我也可以参数化它会很酷)。基本上它应该看起来与提供的图片相同,并且应该能够通过仅提供两点的坐标来绘制。 有没有可能?

【问题讨论】:

  • 我猜你可以将整个事物创建为一个符号,其中包含一个路径,即整个箭头。您可以使用 标记和转换将该符号放置在您想要的任何位置。

标签: svg


【解决方案1】:

坐了几个小时三次检查我所有的数学之后:
在 SVG defs 标签中创建了一个标准化箭头
然后在提供的坐标之后缩放箭头。 (添加了静态高度 XD)

document.addEventListener("DOMContentLoaded", function(event) {
  var svgDoc = document.getElementById("arrowSvg");
  var useArrow = svgDoc.getElementById("customArrow");
  var extraData = useArrow.getAttribute("extra:data");
  extraData = extraData.split(" ");
  var x1 = parseInt(extraData[0]);
  var x2 = parseInt(extraData[1]);
  var y1 = parseInt(extraData[2]);
  var y2 = parseInt(extraData[3]);
  var arrowHeight = 15;
  //Calculate the rotation needed
  var deltaY = y1 - y2;
  var deltaX = x2 - x1;
  var angle = Math.atan2(deltaY, deltaX) * (180 / Math.PI);
  //Distance between the two points.
  var distance = Math.sqrt(Math.pow(x2 - x1, 2) + Math.pow(y2 - y1, 2));
  useArrow.setAttribute("transform", 
                        "translate("+(x1+(deltaX/2))+" "+(y1-(deltaY/2))+") "+
                        "rotate(" + -1*angle +") " +
                        "matrix("+distance+", 0, 0, "+arrowHeight+", "+(0.5-distance*0.5)+","+(0.5-arrowHeight* 0.5)+")");

});
svg {
  width: 50%;
  border: 1px solid black;
}
.arrow {
  stroke: black;
  stroke-width: 0.05;
  fill: yellow;
}
<svg id="arrowSvg" viewBox="0 0 100 100" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" xmlns:extra="ExtraNameSpace">>
  <defs>
    <path id="idArrow" class="arrow" d="M0,0.25 0.60,0.25 
                         0.60,0 1,0.5 0.60,1
                         0.60,0.75 0,0.75z" />
  </defs>
  <!--- Extra Data Param: x1 x2 y1 y2--->
  <use id="customArrow" xlink:href="#idArrow" extra:data="10 90 90 5" />

</svg>

【讨论】:

    【解决方案2】:

    最简单的方法就是使用脚本来创建一个箭头。 这里我简单地从 p1 和 p2 两点确定箭头的长度和角度,然后创建一个正确长度的简单路径,并将其旋转计算出的角度:

    svgns="http://www.w3.org/2000/svg"
    function arrow(p1,p2){
      var h1=15 // line thickness
      var h2=35 // arrow height
      var w2=22 // arrow width
      var deg = Math.atan2(p1.y - p2.y, p1.x - p2.x) * (180 / Math.PI);
      var len = Math.sqrt(Math.pow(p1.y - p2.y,2)+Math.pow(p1.x - p2.x,2))
      var arr = document.createElementNS(svgns,"path")
      var d = `M${p1.x} ${p1.y-h1/2}v${h1}h${h2/2-len}v${(h2-h1)/2}l${-w2} ${-h2/2}l${w2} ${-h2/2}v${(h2-h1)/2}z`
      arr.setAttribute("d",d)
      arr.setAttribute("transform",`rotate(${deg} ${p1.x} ${p1.y})`)
      arr.classList.add("arrow")
      return arr
    }
    
    var a1 = arrow({x:50,y:50},{x:200,y:200})
    var a2 = arrow({x:450,y:50},{x:300,y:200})
    var a3 = arrow({x:450,y:450},{x:300,y:300})
    var a4 = arrow({x:50,y:450},{x:200,y:300})
    svg.appendChild(a1)
    svg.appendChild(a2)
    svg.appendChild(a3)
    svg.appendChild(a4)
    .arrow{stroke-width:3px}
    .arrow:nth-of-type(1){fill:green;stroke:lime}
    .arrow:nth-of-type(2){fill:red;stroke:orange}
    .arrow:nth-of-type(3){fill:blue;stroke:turquoise}
    .arrow:nth-of-type(4){fill:violet;stroke:pink}
    <svg id="svg" viewBox="0 0 500 500" width="400" height="400">
    
    </svg>

    如果您尝试花哨并找到无脚本的解决方案,那么您将不得不跳很多循环... 您将需要至少 4 个箭头,每个箭头从左上到右下,从右上到左下,从左下到右上,从右下到左上...

    这是一个可行的概念证明,但我强烈建议不要这样做......

    svg{overflow:visible;}
    <svg width="200" height="200" style="overflow:visible" stroke="red" color="orange" opacity="0.5">
      <marker id="ah" viewBox="0 0 10 10" orient="auto" refX="10" refY="5" overflow="visible">
        <path d="M0 0L10 5L0 10z"  stroke-width="1"/>
      </marker>
      <marker id="ah2" viewBox="0 0 10 10" orient="auto" refX="10" refY="5">
        <path d="M0 0L10 5L0 10z" fill="currentColor" stroke="none"/>
      </marker>
      <marker id="block" viewBox="0 0 10 10" orient="auto" refX="9" refY="5">
        <rect x="0" y="0" width="10" height="10" stroke="white" stroke-width="1"/>
      </marker>
      <marker id="block2" viewBox="0 0 10 10" orient="auto" refX="9" refY="5">
        <rect x="0" y="0" width="10" height="10" stroke-width="5"/>
      </marker>
      <mask id="m1">
        <rect x="-10%" y="-10%" width="110%" height="110%" fill="white"/>
        <line x1="99.999%" y1="99.999%" x2="100%" y2="100%" stroke-width="20" marker-end="url(#block)"/>
      </mask>
      <line x1="0.001%" y1="0.001%" x2="0%" y2="0%" stroke-width="8" marker-end="url(#block2)"/>
      <line x1="0" y1="0" x2="100%" y2="100%" stroke-width="25"  mask="url(#m1)"/>
      <line x1="99.999%" y1="99.999%" x2="100%" y2="100%" stroke-width="20" marker-end="url(#ah)"/>
      <line x1="0" y1="0" x2="100%" y2="100%" stroke-width="20" stroke="currentColor" mask="url(#m1)"/>
      <line x1="99.999%" y1="99.999%" x2="100%" y2="100%" stroke-width="20" marker-end="url(#ah2)"/>
    </svg>

    【讨论】:

      【解决方案3】:

      我很无聊,所以你去吧。我编写了一个函数来生成正确形状的路径。

      您只需为其指定“from”和“to”坐标、线宽、箭头宽度和箭头长度。

      享受吧!

      var from = {x: 50, y: 250};
      var to = {x: 250, y: 100};
      
      var lineWidth = 30;
      var arrowheadWidth = 60;
      var arrowheadLength = 50;
      
      var svg = document.getElementById("test");
      
      drawArrow(svg, from, to, lineWidth, arrowheadWidth, arrowheadLength);
      
      
      function drawArrow(svg, from, to, lineWidth, arrowheadWidth, arrowheadLength)
      {
        var dx = to.x - from.x;
        var dy = to.y - from.y;
        // Calculate the length of the line
        var len = Math.sqrt(dx * dx + dy * dy);
        if (len < arrowheadLength) return;
      
        // The difference between the line width and the arrow width
        var dW = arrowheadWidth - lineWidth;
        // The angle of the line
        var angle = Math.atan2(dy, dx) * 180 / Math.PI;
        // Generate a path describing the arrow. For simplicity we define it as a
        // horizontal line of the right length, and starting at 0,0. Then we rotate
        // and move it into place with a transform attribute.
        var d = ['M', 0, -lineWidth/2,
                 'h', len - arrowheadLength,
                 'v', -dW / 2,
                 'L', len, 0,
                 'L', len - arrowheadLength, arrowheadWidth / 2,
                 'v', -dW / 2,
                 'H', 0,
                 'Z' ];
        var path = document.createElementNS("http://www.w3.org/2000/svg", "path");
        path.setAttribute("d", d.join(' '));
        path.setAttribute("transform", "translate("+from.x+","+from.y+") rotate("+angle+")");
        path.setAttribute("class", "arrow-line");
        svg.appendChild(path);
      }
      .arrow-line {
        fill: gold;
        stroke: black;
        stroke-width: 6;
      }
      <svg id="test" width="300" height="300">
      </svg>

      【讨论】:

      • 我们有同样的想法,但你的速度更快;-)
      猜你喜欢
      • 2020-09-07
      • 2020-08-27
      • 1970-01-01
      • 1970-01-01
      • 2021-11-23
      • 2020-04-28
      • 1970-01-01
      • 1970-01-01
      • 2014-12-26
      相关资源
      最近更新 更多