为什么<marker>不会定位为<path>

时间:2018-04-25 10:35:25

标签: javascript d3.js svg

我试图用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中使用这个结果时:

&#13;
&#13;
<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>
&#13;
&#13;
&#13;

这是SVG结果

我无法弄清楚标记没有定位的原因。是否有更好的库来生成解决此问题的路径?

3 个答案:

答案 0 :(得分:3)

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

  

曲线两端需要两个额外的点。

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

如果更改曲线,则可以轻松看到此信息。例如,使用curveBasis

&#13;
&#13;
let points = [
    [400,100],
    [450,200],
    [350,200],
    [385,275]
] 
let path = d3.line().curve(d3.curveBasis)(points)

d3.select("#myPath").attr("d", path);
&#13;
<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>
&#13;
&#13;
&#13;

在你的情况下,一个解决方案(可以说是一个黑客)可能会在路径上添加最后一行,距离最终点只有1px:

path = path + "L387,277";

以下是演示:

&#13;
&#13;
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);
&#13;
<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>
&#13;
&#13;
&#13;

答案 1 :(得分:2)

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

SVG使用控制点向量来计算出该点的曲线方向。如果您的控制点向量是从(385,275)到(385,275),那么它无法确定角度。因此它默认为0度的角度。

答案 2 :(得分:-2)

  • 首先,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);});
}