代码之家  ›  专栏  ›  技术社区  ›  ivanross

为什么<marker>没有定位为<path>

  •  4
  • ivanross  · 技术社区  · 8 年前

    我正在尝试使用svg创建一个曲线箭头。我正在使用 d3.line() 生成路径。

    let points = [
        [400,100],
        [450,200],
        [350,200],
        [385,275]
    ] 
    let path = d3.line().curve(d3.curveCardinal)(points)
    
    console.log(path)
    // -> M400,100C400,100,458.3333333333333,183.33333333333334,450,200C441.6666666666667,216.66666666666666,360.8333333333333,187.5,350,200C339.1666666666667,212.5,385,275,385,275
    

    但当我尝试在svg中使用此结果时:

    <svg width="1200" height="1200" viewBox="0 0 1200 1200" xmlns="http://www.w3.org/2000/svg" version="1.1">
            <defs>
                <marker id="Triangle" viewBox="0 0 10 10" refX="1" refY="5" markerWidth="6" markerHeight="6" orient="auto">
                    <path d="M 0 0 L 10 5 L 0 10 z" />
                </marker>
            </defs>
            <path d="M400,100C400,100,458.3333333333333,183.33333333333334,450,200C441.6666666666667,216.66666666666666,360.8333333333333,187.5,350,200C339.1666666666667,212.5,385,275,385,275"
                stroke-width="2" stroke="lightblue" fill="none" style="marker-end: url(#Triangle);"></path>
        </svg>

    下面是SVG结果

    。

    我弄不明白为什么标记没有定位。是否有更好的库来生成解决此问题的路径?

    3 回复  |  直到 8 年前
        1
  •  4
  •   Gerardo Furtado    8 年前

    这是预期的行为。问题是 cardinal spline 。。。

    曲线两端需要另外两个点。

    这些点似乎会干扰标记的方向(确实如此,请参见 LeBeau's answer )。

    如果更改曲线,可以很容易地看到这一点。例如,使用 curveBasis :

    let points = [
        [400,100],
        [450,200],
        [350,200],
        [385,275]
    ] 
    let path = d3.line().curve(d3.curveBasis)(points)
    
    d3.select("#myPath").attr("d", path);
    <script src="https://d3js.org/d3.v5.min.js"></script>
    <svg width="1200" height="1200" viewBox="0 0 1200 1200" xmlns="http://www.w3.org/2000/svg" version="1.1">
        <defs>
            <marker id="Triangle" viewBox="0 0 10 10" refX="1" refY="5" markerWidth="6" markerHeight="6" orient="auto">
                <path d="M 0 0 L 10 5 L 0 10 z" />
            </marker>
        </defs>
        <path id="myPath" stroke-width="2" stroke="lightblue" fill="none" style="marker-end: url(#Triangle);"></path>
    </svg>

    在您的情况下,一种解决方案(可以说是一种破解)可能是在路径上添加一条最后一行,距离最后一点仅1px:

    path = path + "L387,277";
    

    以下是演示:

    let points = [
        [400,100],
        [450,200],
        [350,200],
        [385,275]
    ] 
    let path = d3.line().curve(d3.curveCardinal)(points)
    
    path = path + "L387,277";
    
    d3.select("#myPath").attr("d", path);
    <脚本src=”https://d3js.org/d3.v5.min.js“></script>
    <svg width=“1200”height=“1200”viewBox=“0 0 1200 1200”xmlns=”http://www.w3.org/2000/svg“版本=“1.1”>
    <定义(>);
    <标记id=“Triangle”viewBox=“0 0 10 10”refX=“1”refY=“5”markerWidth=“6”markerHeight=“6”orient=“auto”>
    <路径d=“M 0 0 L 10 5 L 0 10 z”/>
    </标记(>);
    </定义(>);
    <path id=“myPath”stroke width=“2”stroke=“lightblue”fill=“none”style=“marker end:url(#三角形);”&燃气轮机</路径(>);
    </svg>
        2
  •  2
  •   Paul LeBeau    8 年前

    这是因为最后一个控制点和路径的终点具有相同的坐标:(385275)。

    SVG使用控制点向量计算出该点的曲线方向。如果控制点向量从(385275)到(385275),则无法确定角度。因此,默认角度为0度。

        3
  •  -2
  •   ibrahim tanyalcin    8 年前
    • 首先,ref属性在某种程度上是正确的,但我认为可以更好,因为您使用的是完整的viewBox,所以请将refX设置为0。

    • 我认为标记的方向是正确的和更新的。但基于路径的终点,方向的插值可能看起来不正确。因此,您可以通过从最后一个C。。。并将查看方向是否正确。

    • 我进一步测试了它,看看它是否正确,至少对于线段,这里有一把小提琴,我甚至没有使用d3:

    https://jsfiddle.net/ibowankenobi/L8x19rco/2/

    var path = document.querySelector("path[stroke]");
    var arr = Array.apply(null,Array(path.getTotalLength()/4 << 0)).map(function(d,i){
        var p = this.getPointAtLength(i*4);
      return [p.x,p.y];
    },path);
    var length = arr.length;
    animate();
    
    function animate(index){
        if(index >= length){
        return;
      }
        var index = index || 0;
      path.setAttribute("d","M"+arr.slice(1,Math.min(++index+1,length)).join("L"));
        window.requestAnimationFrame(function(){animate(index);});
    }