You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何修复d3绘制Sankey链接时SVG path出现多余圆形的问题

SVG Sankey图路径描边末端多余圆形异常解决方案

问题表现

  • 使用SVG构建Sankey图时,部分<path>元素会在无明确交互/触发条件的情况下,在描边末端出现半径与stroke-width完全一致的多余圆形,不属于预期渲染效果。
  • 异常效果参考:
    示例异常效果图

图中黑色路径为异常元素,标注位置为描边本该终止的边界。

  • 该问题可通过精简代码稳定复现,继续删减代码则无法触发;所有路径由d3计算生成,非手动编写,需要适配d3生成逻辑的通用解决方案,而非针对单个案例的临时修复。

复现代码

<div style="width: 100%; height: 100%;">
  <svg width=100% height=500px>
    <g class="sankey-layer">
      <g class="link-group">
        <path d="M107.61971830985915,30C107.61971830985915,74,107.61971830985915,74,107.61971830985915,118" fill="none" stroke="#000000" stroke-opacity="1" stroke-width="200"></path>
        <path d="M251.11267605633802,30C251.11267605633802,192,328.943661971831,192,328.943661971831,354" fill="none" stroke="#00bbff35" stroke-opacity="0.5" stroke-width="71.74647887323944"></path>
        <path d="M478.38028169014075,30C478.38028169014075,192,384.7464788732394,192,384.7464788732394,354" fill="none" stroke="#00bbff35" stroke-opacity="0.5" stroke-width="39.859154929577464"></path>
        <path d="M542.1549295774647,30C542.1549295774647,192,572.1549295774648,192,572.1549295774648,354" fill="none" stroke="#00bbff35" stroke-opacity="0.5" stroke-width="87.69014084507042"></path>
        <path d="M344.8169014084507,30C344.8169014084507,192,452.50704225352115,192,452.50704225352115,354" fill="none" stroke="#00bbff35" stroke-opacity="0.5" stroke-width="95.66197183098592"></path>
        <path d="M143.49295774647888,148C143.49295774647888,251,153.49295774647888,251,153.49295774647888,354" fill="none" stroke="#00bbff35" stroke-opacity="0.5" stroke-width="79.71830985915493"></path>
        <path d="M409.12773522289996,148C409.12773522289996,251,213.28169014084506,251,213.28169014084506,354" fill="none" stroke="#00bbff35" stroke-opacity="0.5" stroke-width="39.859154929577464"></path>
      </g>
    </g>
  </svg>
</div>

产生原因

该异常是Chrome、Edge等基于Skia渲染引擎的浏览器存在的已知光栅化bug,触发需要同时满足三个条件:

  • 路径使用三次贝塞尔曲线(C指令)绘制,且两个贝塞尔控制点坐标完全重合,属于参数退化的贝塞尔段
  • 路径的描边宽度stroke-width大于路径本身的总长度
  • 路径使用默认的描边线帽、线连接属性,未做显式约束
    三个条件同时满足时,Skia在计算宽描边的轮廓路径时,会在路径终点位置出现切向量计算错误,错误渲染一个直径与stroke-width相等的圆形,该元素不属于路径定义的预期内容。
    d3-sankey默认生成链接路径时,当上下游节点完全对齐(坐标值相等),就会生成两个控制点完全重合的贝塞尔曲线,如果此时节点间距小于链接描边宽度的1/2,就会满足所有触发条件出现该异常。

通用规避方案

以下方案按优先级从高到低排列,优先使用源头修复方案,可100%规避该问题:

  • 从路径生成逻辑层面消除退化贝塞尔段
    对d3生成的路径d属性增加后置校验:遍历所有三次贝塞尔C指令,若检测到两个控制点坐标差小于1e-3(浮点精度阈值),直接将该段贝塞尔曲线替换为直线段(L指令);或修改d3链接生成器的控制点计算逻辑,当上下游节点对齐时,直接生成直线路径而非贝塞尔曲线,从根源上消除触发bug的退化路径段。
  • 给路径增加微小控制点偏移
    若不想修改路径类型,可在检测到两个控制点重合时,给其中一个控制点增加0.01px级别的坐标偏移,该偏移量极小,肉眼完全无法识别路径形状变化,但可以破坏贝塞尔段的参数退化条件,避免Skia的计算错误。
  • 布局层面增加最小间距约束
    在Sankey布局计算阶段,增加相邻节点的最小间距校验,保证相邻节点的间距大于对应链接描边宽度的0.6倍,避免描边宽度超过路径总长度的场景出现。
  • 显式设置描边属性降低触发概率
    给所有链接路径统一添加描边属性:stroke-linecap: butt; stroke-linejoin: miter; stroke-miterlimit: 2;,强制使用平端线帽、锐角连接,可降低异常出现的概率,但无法100%规避内核层面的光栅化错误,仅作为辅助方案使用。

内容的提问来源于stack exchange,提问作者Spenhouet

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.30 17:24:20