SVG path 指令與 Bézier 曲線:S/T 接點、橢圓弧 A 及圓形近似
DDEVELOPER
開發發布: 10 分鐘閱讀

SVG path 指令與 Bézier 曲線:S/T 接點、橢圓弧 A 及圓形近似

SVG path 用一串指令描述移動、直線、曲線及封閉路徑。例如 M10,10 C20,20 40,20 50,10,只要分清目前位置與控制點,就能逐段理解。

本文以 W3C SVG 2 的 Paths 章(2018 年 10 月 4 日候選推薦標準)及 SVG 1.1 橢圓弧實作說明為依據,整理計算方式與編輯時容易混淆的條件。

日文原文發布: 2026-04-25

10 類指令與絕對、相對座標

大寫指令一般使用絕對座標,小寫則使用相對目前位置的座標。Z/z 沒有座標參數,兩者作用相同。

如表格未能完整顯示,可左右捲動。

指令參數作用
M/mx y移動,不畫線
L/lx y直線
H/hx水平線
V/vy垂直線
C/cx1 y1 x2 y2 x y三次 Bézier 曲線
S/sx2 y2 x y按前一段 C/S 決定首個控制點
Q/qx1 y1 x y二次 Bézier 曲線
T/tx y按前一段 Q/T 決定控制點
A/arx ry rotation large sweep x y橢圓弧
Z/z封閉目前子路徑

相同指令的參數可連續書寫,例如 L 10,20 30,40 50,60 是三段線。M 後第二組及其後的座標當作 L 處理。Z 會把描邊兩端作封閉接合,與用 L 返回起點但保留開放端點不同。

三次 Bézier 曲線的四個控制點

三次曲線由 P₀、P₁、P₂、P₃ 四個控制點決定,參數 t∈[0,1]:

B(t) = (1-t)³ P₀ + 3(1-t)² t P₁ + 3(1-t) t² P₂ + t³ P₃

這是 Bernstein 基底的加權和。曲線在 t=0 和 t=1 通過兩端點,但通常不通過中間兩個控制點;整條曲線位於四點的凸包內。平移、旋轉或縮放控制點,亦會對曲線作相同的仿射變換。

端點導數為 B′(0)=3(P₁−P₀)、B′(1)=3(P₃−P₂)。若向量為零,不能只靠這兩式判定切線方向。C 的目前位置是 P₀,其餘三組參數依次指定 P₁、P₂ 和 P₃。

用 de Casteljau 評估及分割曲線

de Casteljau 反覆作線性插值,比直接展開多項式更適合穩定地評估曲線,但仍會受浮點捨入影響。以下是偽代碼,lerp(a,b,t) 表示 (1−t)a+tb:

function deCasteljau(P0, P1, P2, P3, t):
  Q0 = lerp(P0, P1, t) // (1-t)P0 + t P1
  Q1 = lerp(P1, P2, t)
  Q2 = lerp(P2, P3, t)
  R0 = lerp(Q0, Q1, t)
  R1 = lerp(Q1, Q2, t)
  return lerp(R0, R1, t)

保留中間結果,可在指定 t 將曲線分成兩條 Bézier 曲線;精確算術下形狀不變。t=0.5 的分割可作為編輯器在曲線中間加點的數學基礎,但不代表所有編輯器的操作都採用相同實作。

S/T 並非任何情況都會平滑接駁

S 只有在前一個指令是 C 或 S 時,才把上一段最後的控制點以目前位置為中心反射,作為新一段的首個控制點;否則首個控制點就是目前位置。T 也只有在 Q 或 T 之後才反射,其他情況以目前位置作控制點。

reflectedX = 2 * currentX - previousControlX
reflectedY = 2 * currentY - previousControlY

M 0,0 C 10,10 30,10 40,20 S 60,10 70,20
M 0,0 C 10,10 30,10 40,20 C 50,30 60,10 70,20

兩條 path 表示相同曲線。相同次數、每段 t∈[0,1] 的參數化下,反射使接點的一階導數相同;不能略去前一個指令的條件,便宣稱所有接點都平滑。

橢圓弧 A 的七個參數

A 使用以下參數:

A rx ry x-axis-rotation large-arc-flag sweep-flag x y
  • rx、ry 是橢圓兩軸的半徑。
  • x-axis-rotation 是橢圓旋轉角,以度表示。
  • large-arc-flag 為 0 時選擇 180 度或以下的弧,為 1 時選擇 180 度或以上的弧;半圓時可能重合。
  • sweep-flag 為 0 表示負角度方向,為 1 表示正角度方向。在一般 y 軸向下的 SVG 座標系中,分別看起來是逆時針和順時針。
  • x、y 是終點。

一般情況最多有四個候選弧,但半徑修正後可能重合。負半徑按絕對值處理;任一半徑為零時改畫直線;起點與終點相同則省略該弧。單一 A 指令不能畫出完整圓形。

由端點形式換算中心與角度

SVG A 以端點、半徑、旋轉及旗標指定弧;需要中心形式的程式則要換算中心、起始角及掃掠角。SVG 1.1 附錄 F.6.5 提供這項轉換。

以下偽代碼中,(x1,y1) 和 (x2,y2) 是起點與終點,fA、fS 分別是 large-arc-flag、sweep-flag,phi 是已換成弧度的旋轉角。xp、yp 是轉換後的起點,cxp、cyp 是轉換座標中的中心;cx、cy 是原座標中的中心。^ 表示乘方,不是 JavaScript 的 XOR。

// 先處理相同端點和零半徑;rx、ry 取絕對值,phi 使用弧度
xp = cos(phi)*(x1-x2)/2 + sin(phi)*(y1-y2)/2
yp = -sin(phi)*(x1-x2)/2 + cos(phi)*(y1-y2)/2
lambda = (xp/rx)^2 + (yp/ry)^2
if lambda > 1:
  rx *= sqrt(lambda)
  ry *= sqrt(lambda)
sign = (fA != fS) ? +1 : -1
factor = sign * sqrt(max(0, (rx^2*ry^2 - rx^2*yp^2 - ry^2*xp^2) / (rx^2*yp^2 + ry^2*xp^2)))
cxp = factor * rx*yp / ry
cyp = factor * -ry*xp / rx
cx = cos(phi)*cxp - sin(phi)*cyp + (x1+x2)/2
cy = sin(phi)*cxp + cos(phi)*cyp + (y1+y2)/2
start = atan2((yp-cyp)/ry, (xp-cxp)/rx)
sweep = atan2((-yp-cyp)/ry, (-xp-cxp)/rx) - start
if !fS and sweep > 0: sweep -= 2*pi
if  fS and sweep < 0: sweep += 2*pi

當兩端點無法放入原半徑的橢圓時,先按 lambda 放大半徑。max(0,…) 避免捨入造成極小負數而無法開平方。極端數值仍可能溢位,因此這不是可直接取代完整數值函式庫的程式,也不是特定編輯器的內部實作保證。

四種圓形表示:精確與近似要分開

以下以 (100,100) 為中心、半徑 50 作比較。依次為 circle 元素、兩段 A、四段 C 近似及四段 Q 近似;circle 本身不是 path。

<circle cx="100" cy="100" r="50"/>

<path d="M 50,100 A 50,50 0 0 1 150,100 A 50,50 0 0 1 50,100"/>

<path d="M 100,50 C 127.614237,50 150,72.385763 150,100 C 150,127.614237 127.614237,150 100,150 C 72.385763,150 50,127.614237 50,100 C 50,72.385763 72.385763,50 100,50"/>

<path d="M 100,50 Q 150,50 150,100 Q 150,150 100,150 Q 50,150 50,100 Q 50,50 100,50"/>

四段 C 採用經典係數 κ=4(√2−1)/3≈0.5523。未捨入係數的最大徑向誤差約為半徑的 0.0273%;這並非所有誤差準則下的最優係數。四段 Q 的誤差更大,形狀較接近圓角方形。一般多項式 Bézier 曲線不是精確圓弧。

SVGO 的 convertShapeToPath 可透過 convertArcs 設定把 circle/ellipse 轉為 path,但不是永遠轉成四段 C。改換表示方式亦不保證檔案更小。

位置、切線與導數的連續性

每段參數均取 t∈[0,1] 時,可分開比較以下條件:

如表格未能完整顯示,可左右捲動。

條件含義
G⁰接點位置相同,未指定有沒有角
位置及切線方向相同,假設接點正則
位置及一階導數相同
符合 C¹,且二階導數相同

幾何上的曲率連續與 C² 並不等同。S/T 在上述條件下只保證一階導數相同,不會自動帶來 C²。SVG path 沒有直接指定 B-spline 或 NURBS 的指令,須轉成支援的分段或近似表示。

縮短 path 不等於完全保留效果

  • 省略重複指令,例如將 L 10,10 L 20,20 寫成 L 10,10 20,20。
  • 按字串長度選用絕對或相對座標。
  • 減少小數位,但要接受可能改變形狀。
  • 在適合時把基本形狀轉為 path。
  • 控制點符合反射條件時,用 S/T 簡寫。
  • 合併可安全合併的 path。

省略指令與捨入座標是不同操作。合併可能影響填色、標記、動畫或外部參照。保留原圖,按實際縮放比例和使用位置比較結果,不要把固定縮減率或完全相同外觀當作保證。

重點整理

  • 10 類指令各有規則,M 的後續座標與 Z 的封閉尤其要留意。
  • Bézier 曲線由控制點決定,可用 de Casteljau 評估及分割。
  • S/T 的控制點反射取決於前一個指令。
  • A 要處理退化情況、半徑修正、旗標及座標方向。
  • 圓形近似、座標捨入與精確形狀保存並不相同。

參考資料及來源

編輯說明

本文使用 AI 協助編輯,並在發布前由編輯核對。內容仍可能包含事實、理解或時效上的錯誤;作出重要決定前,請查閱所列的一手資料或官方文件。

相關文章