D3 弧形端部人字形

D3 Arc With Chevron Shaped End

我使用 D3.js 绘制了一个 arc,默认情况下它的末端是方形的。

var arc = d3.arc()
    .innerRadius(0)
    .outerRadius(100)
    .startAngle(0)
    .endAngle(Math.PI);
d3.selectAll('svg')
    .append('path')
    .attr('d', function() {
        return arc();
    });

如何在一端画一个人字形的圆弧。

我想我明白你在找什么,所以我试试看:

正如您可能从 d3.js 文档中猜到的那样,d3.arc() 没有在一端提出观点所需的方法。两端都加了填充和圆角,我看不出它们如何在两端形成一个点,更不用说一个了。

想到了两个解决方案(可能还有很多我什至无法想象)

  1. 根据每个圆弧的端角剪掉每个圆弧的端点,并附加一个三角形或其他类似的形状(或者,将某种掩码应用到 trim 端点成一个点)
  2. 尝试根据您的需要修改 d3.arc(),以模块化方式接受 develop/refine d3 的邀请。

就我个人而言,我认为选项一可能不太干净,而且可能更难设计。选项二应该是可行的,并且在这种鼓励下潜入并制作模块:

Small files are nice, but modularity is also about making D3 more fun. Microlibraries are easier to understand, develop and test. They make it easier for new people to get involved and contribute. They reduce the distinction between a “core module” and a “plugin”, and increase the pace of development in D3 features. (https://github.com/d3/d3/blob/master/CHANGES.md)

我想我会试一试。


我整理了一个尝试,可能是基于 d3.arc() 函数的人字形尖弧模块的开始。

d3-shape.js 模块中 d3.arc() 函数的圆角部分可能是最好看的地方,因为它显示了对圆弧末端的修改。修改圆弧的模块部分,如果是圆角,看起来像:

      context.arc(t0.cx, t0.cy, rc1, Math.atan2(t0.y01, t0.x01), Math.atan2(t0.y11, t0.x11), !cw);
      context.arc(0, 0, r1, Math.atan2(t0.cy + t0.y11, t0.cx + t0.x11), Math.atan2(t1.cy + t1.y11, t1.cx + t1.x11), !cw);
      context.arc(t1.cx, t1.cy, rc1, Math.atan2(t1.y11, t1.x11), Math.atan2(t1.y01, t1.x01), !cw);

首先处理外边缘(如上所示)。第一行是后外角的倒圆,第三行是前外角的倒圆。简单地删除第三条线允许一个尖弧(如果你也从内边缘删除它)。然后剩下的挑战是使圆弧的另一端平坦,我通过使用起始角度和内外半径找到圆弧的角来创建平坦的末端。

最终结果是这样的:

// get tail coordinate (outer)
    var tailOuter = {};
    tailOuter.x = Math.cos(a0) * r1; // a0 = starting angle
    tailOuter.y = Math.sin(a0) * r1; // r1 = outer radius

     context.moveTo(tailOuter.x, tailOuter.y);
     context.arc(0, 0, r1, Math.atan2(t0.cy + t0.y11, t0.cx + t0.x11), Math.atan2(t1.cy + t1.y11, t1.cx + t1.x11), !cw);

我整理了一个快速而肮脏的 module,它采用 d3.arc() 函数并创建了一个 d3.cheveronArc() 函数。它是对 d3.arc() 的彻底修改,只有四种方法(inner/outerRadius()start/endAngle())。它无法检查可能导致错误行为的参数(例如:人字形比弧长)。这只是一个概念证明,尽管我对它看起来相当快速的尝试感到满意:

你可能会注意到,最里面的圆在它的尾部附近有一个奇怪的形状,小的内半径似乎会导致这样的问题。

代码可以查看: http://bl.ocks.org/andrew-reid/3375e602cc6c00c4e3ea4799d171ee27

看着看着,感觉要在圆弧的尾端加入倒V字的选项,视觉效果更好,但那是另外一回事了。

我只会使用带有 SVG 标记的 d3 路径生成器。将任何形状添加到任何路径。通过编辑标记定义来编辑 "ends"。使用 D3 路径生成器来定义您的弧线(或任何路径)。

值得注意的是,如果您采用这种方法,则必须使用 d3 路径生成器而不是 d3 弧线生成器,因为弧线生成器会隐式关闭路径(将您的 "end" 标记放回路径)。

在我的示例中,我也将人字形添加到开头,只是为了表明它与添加 .attr("marker-start","url(#chevron)").attr("marker-end","url(#chevron)")

一样简单

D3 路径生成器 | https://github.com/d3/d3-path/blob/master/README.md#path

SVG 标记 | https://developer.mozilla.org/en-US/docs/Web/SVG/Element/marker


edit: 现在我想到了,您可以使用 d3.symbols 为您生成 markers/ends 而不是手动定义形状小路。人字形必须是自定义的,但您可以使用三角形符号。

D3 符号 | https://github.com/d3/d3-shape#symbols


console.clear()

var path = d3.path()
path.arc(225,80,70,1,-.5)

var path2 = d3.path()
path2.moveTo(20,20)
path2.bezierCurveTo(150,300,200,0,450,100)

d3.select("svg").append("path")
  .attr("d", path2.toString())
  .attr("stroke","steelblue")
  .attr("fill","none")
  .attr("stroke-width","20")
  .attr("marker-start","url(#chevron)")
  .attr("marker-end","url(#chevron)")

d3.select("svg").append("path")
  .attr("d", path.toString())
  .attr("stroke","#43A2CA")
  .attr("fill","none")
  .attr("stroke-width","20")
  .attr("marker-start","url(#chevron)")
  .attr("marker-end","url(#chevron)")
<script src="https://unpkg.com/d3@4.4.0"></script>
<?xml version="1.0"?>
<svg width="500" height="200" viewBox="0 0 500 200">

  <defs>
    <marker id="chevron"
      viewBox="0 0 20 20" refX="10" refY="10" 
      markerUnits="userSpaceOnUse"
      markerWidth="20" markerHeight="20"
      orient="auto" fill="black">
      <path d="M0 0 10 0 20 10 10 20 0 20 10 10Z" />
    </marker>
  </defs>

</svg>