【问题标题】:Thick Bezier curves in SVG without artifactsSVG 中没有伪影的粗贝塞尔曲线
【发布时间】:2016-03-19 21:44:49
【问题描述】:

我尝试画粗Bezier lines(用于自定义Sankey diagram)。 我使用SVG Paths,贝塞尔曲线的形式为C x1 y1, x2 y2, x y。我使用stroke 而不是fill,因此它们具有恒定的宽度(并且可以表示流)。

如果线条很细或垂直​​差异相对较小,它会很好地工作。但是,如果它们很厚,我会得到一些令人讨厌的伪影(看起来像牛角) - 请参见下图中右下角的曲线:

来源:http://jsfiddle.net/stared/83jr5fub/

有没有办法避免工件,即:

  • 确保x1 左侧或x 右侧没有任何内容,
  • 左右实际宽度匹配stroke-width?

【问题讨论】:

  • 我个人没有得到任何文物。您是否尝试过其他浏览器或机器?
  • @MonicaOlejniczak 你的意思是在 JSFiddle 上?上面是一个屏幕截图(我所说的人工制品是指“角”。)在浏览方面——我在 Chrome、Firefox 和 Safari 上看起来是一样的。
  • 也许这个小提琴会让发生的事情更清楚:jsfiddle.net/83jr5fub/2
  • @Kaiido 我知道发生了什么(但非常感谢这个例子 - 条纹使它清晰可见!)。我的问题是如何避免这种行为;我确实尝试使用fill 形状,但它们的宽度明显不均匀(尽管可能进行一些调整会有所帮助)。使用stroke - 我想也许一些面具会有所帮助。
  • 转储可能但未经测试的解决方案:stroke-linecap: rect 但这会在路径的起点和终点添加笔画宽度。做一个封闭的路径并填充它。确保你的笔画宽度永远不会超过角度......我不知道如何清楚地说,但只要确保这个小提琴中的破折号永远不会崩溃:@ 987654327@ 这几乎就是我能想到的全部。我认为面具不会有帮助,因为我认为你也需要封闭路径。

标签: css d3.js svg


【解决方案1】:

Kaiido 给出了一个出色而完整的答案,说明为什么带有粗笔划宽度的 SVG 路径会显示有伪影以及如何避免这种情况。我将尝试提供更多特定于 D3.js 桑基图的信息,因为我最近遇到了与 Piotr Migdal 相同的问题。

原始桑基图代码

(来自this Sankey example 中的 Sankey.js,类似于 Piotr Migdal 提到的示例)

  // regular forward node
  var x0 = d.source.x + d.source.dx,
      x1 = d.target.x,
      xi = d3.interpolateNumber(x0, x1),
      x2 = xi(curvature),
      x3 = xi(1 - curvature),
      y0 = d.source.y + d.sy + d.dy / 2,
      y1 = d.target.y + d.ty + d.dy / 2;
  return "M" + x0 + "," + y0
       + "C" + x2 + "," + y0
       + " " + x3 + "," + y1
       + " " + x1 + "," + y1;

修改代码

  // regular forward node
  var x0 = d.source.x + d.source.dx,
      x1 = d.target.x,
      xi = d3.interpolateNumber(x0, x1),
      x2 = xi(curvature),
      x3 = xi(1 - curvature),
      y0 = d.source.y + d.sy,
      y1 = d.target.y + d.ty;
  return "M" + x0 + "," + y0
       + "C" + x2 + "," + y0
       + " " + x3 + "," + y1
       + " " + x1 + "," + y1
       // move down for the wanted width
       + "l" + 0  + "," + d.dy
       // draw another path below mirroring the top
       + "C" + x3 + "," + (y1 + d.dy)
       + " " + x2 + "," + (y0 + d.dy)
       + " " + x0 + "," + (y0 + d.dy);

那么你还需要改变你的css:

  • 中风:无
  • 设置填充颜色

并删除任何设置 HTML 元素笔画宽度的 D3 代码。

【讨论】:

    【解决方案2】:

    我认为在您的情况下(使用给定路径)他最好的解决方案是使您的路径关闭,并使用它的填充属性。

    为此,您必须在BezierCurveTo 的末尾添加一个lineTo(0, strokeWidth),然后以另一种方式重新绘制贝塞尔曲线:

    var svg = d3.select("#chart");
    
    var data = [
    	{t: 5, dy: 10},
    	{t: 5, dy: 20},
    	{t: 5, dy: 40},
    	{t: 20, dy: 10},
    	{t: 20, dy: 20},
    	{t: 20, dy: 40},
    	{t: 50, dy: 10},
    	{t: 50, dy: 20},
    	{t: 50, dy: 40},
    ];
    
    var ctrl = 10;
    var dx = 40;
    var spacing = 100;
    var colors = d3.scale.category10();
    
    svg
      .attr("width", 4 * spacing)
      .attr("height", 4 * spacing);
    
    svg.selectAll("path")
      .data(data)
      .enter()
      	.append("path")
        .attr("d", function (d, i) {
          var x1 = spacing + spacing * (i % 3);
          var y1 = spacing + spacing * Math.floor(i / 3);
          return "M" + x1 + "," + y1 +
          "c" + ctrl + "," + 0 +
          " " + (dx - ctrl) + "," + d.dy +
          " " + dx + "," + d.dy +
          // move down for the wanted width
          "l" + (0) + "," + (d.t) +
          // negate all values
          "c" + (ctrl * -1) + "," + 0 +
          " " + ((dx - ctrl) * -1) + "," + (d.dy * -1) +
          " " + (dx * -1) + "," + (d.dy * -1);
      })
      .style("fill", colors(0))
    <script src="https://cdnjs.cloudflare.com/ajax/libs/d3/3.4.11/d3.min.js"></script>
    <svg id="chart"></svg>

    因为这里有一个价值超过 10000 字的动画,它显示了正在发生的事情以及为什么它不能被称为浏览器错误:

    @keyframes dash {
      from {
         stroke-dashoffset: -10%;
      }
       to {
         stroke-dashoffset: 90%;
      }
     }
    @-webkit-keyframes dash {
      from {
         stroke-dashoffset: -10%;
      }
       to {
         stroke-dashoffset: 90%;
      }
     }
    #dashed{
      animation : dash 12s linear infinite;
      }
    <svg height="200" width="200" id="chart" viewBox="290 260 100 100">
    <path id="dashed" style="fill: none; stroke: rgb(31, 119, 180); stroke-width: 50; stroke-dasharray: 3, 3;" d="M300,300c10,0 30,40 40,40"></path>
    <path style="fill: none; stroke: black;" d="M300,300c10,0 30,40 40,40">
    </path></svg>

    【讨论】:

    • 非常漂亮的动画值得那 10 000 字
    • 这很好,但有一个视觉问题 - 线条在中间变细,例如jsfiddle.net/stared/ufms2tpe(其实我一开始是用形状的,后来才发现可以用很粗的笔触)。我试图使控件不均匀,但找不到适用于各种厚度和dys 的好的公式。
    • @PiotrMigdal,我明白了......实际上线条在中间并没有变得“更细”,只是它们的“重量”总是垂直的(矢量 0,strokeWidth )。你想要什么需要计算正确的角度,并在这个角度做重量。这就是中风的作用。但是随着你的曲线,它会导致你得到奇怪的东西。我看不到这条路径的正确解决方案...
    猜你喜欢
    • 1970-01-01
    • 2017-11-18
    • 2021-09-10
    • 1970-01-01
    • 1970-01-01
    • 2022-07-11
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多