vatt'ghern jaskier's ballads

Cookbook snippet · tier 2

SVG 路徑變形

在兩個 SVG path d 屬性之間內插,做出平滑的形狀過場。

Live example

transition: d 平滑插值需要兩個條件:

  1. 兩個 d 的命令數一致(M / C / L / Z 數量與順序相同)。
  2. 每個 segment 的端點在兩個 d 裡 *同位置*。

本檔三個 shape 共用 4 個軸向端點,只動 cubic 控制點 → 圓 → soft → 端點重合(diamond)。

完整食譜 (HTML + JS · 複製改寫用)

Interpolate between two SVG path d attributes for smooth shape transitions.

When to use

  • Smooth transition between two architecture states
  • Animated graph layout (force-directed settle)
  • Morphing one curve into another in a chart (before/after view)

Complete snippet (paste-and-tweak)

<figure class="vg-w-morph-EXAMPLE">
  <svg viewBox="0 0 200 100">
    <path id="vg-w-morph-EXAMPLE-p" d="M 10 50 Q 100 10 190 50" fill="none" stroke="var(--accent)" stroke-width="2" />
  </svg>
  <button id="vg-w-morph-EXAMPLE-toggle">morph</button>
  <script>
    (function () {
      const root = document.querySelector('.vg-w-morph-EXAMPLE');
      const path = root.querySelector('#vg-w-morph-EXAMPLE-p');
      const btn = root.querySelector('#vg-w-morph-EXAMPLE-toggle');
      const A = 'M 10 50 Q 100 10 190 50';
      const B = 'M 10 50 Q 100 90 190 50';
      let toB = true;
      btn.addEventListener('click', () => {
        path.animate(
          [{ d: `path('${toB ? A : B}')` }, { d: `path('${toB ? B : A}')` }],
          { duration: 600, easing: 'ease-in-out', fill: 'forwards' }
        );
        toB = !toB;
      });
    })();
  </script>
</figure>

Gotchas

  • Path command compatibility: morph only works smoothly when both paths have the same command sequence (same number of M/L/Q/C segments). If they differ, browsers fall back to a jumpy linear interpolation.
  • path('...') CSS syntax is needed inside WAAPI keyframes for the d attribute. Without it, the browser treats d as a generic string and animates per-character (broken).
  • Falling back to manual interpolation: if browsers don't morph cleanly, parse both paths into segments and lerp coordinates in rAF — more code but bulletproof.