【SVG】SVG的奪命利器——path

歸子莫發表於2021-11-11

【SVG】SVG的奪命利器——path

部落格說明

文章所涉及的資料來自網際網路整理和個人總結,意在於個人學習和經驗彙總,如有什麼地方侵權,請聯絡本人刪除,謝謝!

說明

昨天一發布,突然看到有朋友留言,希望看到更多的SVG的文章。突然有些感動?,那麼繼續。(感動點比較低哈)

path元素的能力

path元素是SVG基本形狀中最強大的一個,它不僅能建立其他基本形狀,還能建立更多其他形狀。

比如矩形(直角矩形或者圓角矩形)、圓形、橢圓、折線形、多邊形等。

更重要的是能夠繪製一些曲線,如貝塞爾曲線、二次曲線等。

path元素的形狀是通過屬性d來定義的,d屬性通過“命令和座標”的序列來控制整個path繪製的路徑

path的座標命令

先採用總分的形式吧。

  • M = moveto
  • L = lineto
  • H = horizontal lineto
  • V = vertical lineto
  • C = curveto
  • S = smooth curveto
  • Q = quadratic Bézier curve
  • T = smooth quadratic Bézier curveto
  • A = elliptical Arc
  • Z = closepath

然後一個個來介紹主要分為直線命令和曲線命令

直線命令

直線命令主要有以下幾種:

  • M(moveto):需要兩個引數(x軸和y軸座標,移動到的點的x軸和y軸的座標
  • L(lineto):需要兩個引數(x軸和y軸座標),它會在當前位置和最新的位置(L前面畫筆所在的點)之間畫一條線段。
  • H(horizontal lineto):一個引數,標明在x軸移動到的位置,繪製水平線
  • V(vertical lineto):一個引數,標明在y軸移動到的位置,繪製垂直線
  • Z( closepath):從當前點畫一條直線到路徑的起點

示例:

畫一個正方形

<svg width="100px" height="100px" version="1.1" xmlns="http://www.w3.org/2000/svg">
  <path d="M10 10 H 90 V 90 H 10 L 10 10"/>
</svg>

效果:

image-20211111180744092

程式碼解析:

首先定義了一個100x100的畫布(座標系),用M命令在(10,10)建立起點,通過H命令在水平方向移動到x軸為90的位置,y軸不變,也就是移動到(90, 10),再通過V命令移動到y軸為90的位置,x軸不變,也就是座標為(90,90)的位置,再通過H命令在水平方向移動到x軸為10的位置,y軸不變,此刻的位置為(10,90),最後使用L命令在起點(10,10)的位置與上次的點(10,90)畫一條直線,那麼四條邊就畫完了。

思考?

通過上面的引數和示例可以想到,其實最後的一步有幾種辦法可以實現

<svg width="100px" height="100px" version="1.1" xmlns="http://www.w3.org/2000/svg">
	<!-- 利用Z命令 -->
  <path d="M10 10 H 90 V 90 H 10 Z"/>
  <!-- 利用V命令 -->
  <path d="M10 10 H 90 V 90 H 10 V 10"/>
</svg>

實現的效果都是一樣的,Z命令是直接從當前點畫一條直線到起點,V利用本次示例中最後一步向垂直方向移動,所以它也能夠做到。

以上所說的是絕對距離,當然還可以使用相對距離來畫,使用小寫字母

<svg width="100px" height="100px" version="1.1" xmlns="http://www.w3.org/2000/svg">
	<path d="M10 10 h 80 v 80 h -80 Z" />
</svg>
曲線命令

C(curveto)三次貝塞爾曲線

三次貝塞爾曲線需要定義一個點和兩個控制點,用C命令來建立。

(x,y)表示的是曲線的終點,(x1,y1)是起點的控制點,(x2,y2)是終點的控制點。控制點描述的是曲線起始點的斜率,曲線上各個點的斜率,是從起點斜率到終點斜率的漸變過程。

命令引數:

C x1 y1, x2 y2, x y 
c dx1 dy1, dx2 dy2, dx dy

示例:

<svg width="200px" height="200px" version="1.1" xmlns="http://www.w3.org/2000/svg">
	<path d="M10 10 C 20 20, 40 20, 50 10" stroke="black" fill="transparent"/>
</svg>

效果:

image-20211111214258768

程式碼解釋:

用M建立一個起點(10,10),C建立一個以(50,10)為終點,(20,20)為起點的控制點,(40,10)為終點的控制點。

給它加上輔助的點,看效果和程式碼

<svg width="200px" height="200px" version="1.1" xmlns="http://www.w3.org/2000/svg">
    <path d="M130 110 C 120 140, 180 140, 170 110" stroke="black" fill="transparent"/>
    <circle cx="130" cy="110" r="2" fill="red"/>
    <circle cx="120" cy="140" r="2" fill="red"/>
    <line x1="130" y1="110" x2="120" y2="140" style="stroke:rgb(255,0,0);stroke-width:2"/>
    <circle cx="180" cy="140" r="2" fill="red"/>
    <circle cx="170" cy="110" r="2" fill="red"/>
    <line x1="180" y1="140" x2="170" y2="110" style="stroke:rgb(255,0,0);stroke-width:2"/>
</svg>

image-20211111214528381

原理分析:結合下面的圖看一下,曲線沿著起點到第一控制點的方向伸出,逐漸彎曲,然後沿著第二控制點到終點的方向結束。

image-20211111214622149

S(smooth curveto)簡寫的三次貝塞爾曲線

S其實是建立一個特殊的三次貝塞爾曲線。它特殊的地方就是當一個點某一側的控制點是它另一側的控制點的對稱也就是保持斜率不變。

(x,y)表示的是曲線的終點,(x2,y2)是既是終點的控制點也是起點的控制點。

命令引數:

S x2 y2, x y
s dx2 dy2, dx dy

示例:

先畫一個三次貝塞爾曲線

<svg width="400px" height="200px" version="1.1" xmlns="http://www.w3.org/2000/svg">
	<path d="M 10 50 C 40 20, 120 20, 150 50" stroke="black" fill="transparent"/>
</svg>

效果:

image-20211111220604444

在三次貝塞爾曲線後面使用簡寫的三次貝塞爾曲線,使用S命令畫一個簡寫的三次貝塞爾曲線。

<svg width="400px" height="200px" version="1.1" xmlns="http://www.w3.org/2000/svg">
	<path d="M 10 50 C 40 20, 120 20, 150 50 S 260 80, 290 50" stroke="black" fill="transparent"/>
</svg>

效果:

相當於起始點使用上一個三次貝塞爾曲線終點的斜率,依照這個斜率畫出了一個映象的三次貝塞爾曲線。

image-20211111220528146

將輔助點標示出來

<svg width="400px" height="200px" version="1.1" xmlns="http://www.w3.org/2000/svg"> 
	<path d="M 10 50 C 40 20, 120 20, 150 50 S 260 80, 290 50" stroke="black" fill="transparent"/>
	<circle cx="10" cy="50" r="2" fill="red"/>
    <circle cx="40" cy="20" r="2" fill="red"/>
    <line x1="10" y1="50" x2="40" y2="20" style="stroke:rgb(255,0,0);stroke-width:1"/>

    <circle cx="120" cy="20" r="2" fill="red"/>
    <circle cx="150" cy="50" r="2" fill="red"/>
    <line x1="120" y1="20" x2="150" y2="50" style="stroke:rgb(255,0,0);stroke-width:1"/>

    <circle cx="180" cy="80" r="2" fill="blue"/>
    <circle cx="180" cy="80" r="2" fill="red"/>
    <circle cx="260" cy="80" r="2" fill="red"/>
    <line x1="150" y1="50" x2="180" y2="80" style="stroke:blue;stroke-width:1"/>
    <line x1="290" y1="50" x2="260" y2="80" style="stroke:rgb(255,0,0);stroke-width:1"/>
</svg>

效果:

image-20211111223144605

藍色的線連線的那個點,其實就是向上一個“借”來的。

注意:如果S命令跟在一個C命令或者另一個S命令的後面,它的第一個控制點,就會被假設成前一個控制點的對稱點。如果S命令單獨使用,前面沒有C命令或者另一個S命令,那麼它的兩個控制點就會被假設為同一個點。

Q(quadratic Bézier curve)二次貝塞爾曲線

二次貝塞爾曲線Q,只需要一個控制點,用來確定起點和終點的曲線斜率。因此它需要兩組引數,控制點和終點座標。

Q x1 y1, x y
q dx1 dy1, dx dy

示例:

<svg width="200px" height="200px" version="1.1" xmlns="http://www.w3.org/2000/svg">
    <path d="M 10 80 Q 95 20 180 80" stroke="black" fill="transparent"/>
</svg>  

效果:

image-20211111221937262

它相比三次貝塞爾曲線,只有一個控制點,也就是一個點同時控制起點和終點,將輔助點標出來

<svg width="200px" height="200px" version="1.1" xmlns="http://www.w3.org/2000/svg">
    <path d="M 10 80 Q 95 20 180 80" stroke="black" fill="transparent"/>
    <circle cx="10" cy="80" r="2" fill="red"/>
    <circle cx="95" cy="20" r="2" fill="red"/>
    <circle cx="180" cy="80" r="2" fill="red"/>
    <line x1="10" y1="80" x2="95" y2="20" style="stroke:rgb(255,0,0);stroke-width:1"/>
    <line x1="95" y1="20" x2="180" y2="80" style="stroke:rgb(255,0,0);stroke-width:1"/>
</svg>  

效果:

image-20211111222246567

T(smooth quadratic Bézier curveto)簡寫的二次貝塞爾曲線

簡寫的二次貝塞爾曲線T,只需要一個終點。

T x y
t dx dy

示例:

<svg width="400px" height="200px" version="1.1" xmlns="http://www.w3.org/2000/svg">
    <path d="M10 80 Q 50 10, 90 80 T 170 80" stroke="black" fill="transparent"/>
</svg>

效果:

image-20211111223929914

加上輔助線和點

<svg width="400px" height="200px" version="1.1" xmlns="http://www.w3.org/2000/svg">
    <path d="M10 80 Q 50 10, 90 80 T 170 80" stroke="black" fill="transparent"/>

    <circle cx="10" cy="80" r="2" fill="red"/>
    <circle cx="50" cy="10" r="2" fill="red"/>
    <line x1="10" y1="80" x2="50" y2="10" style="stroke:rgb(255,0,0);stroke-width:1"/>

    <circle cx="90" cy="80" r="2" fill="red"/>
    <line x1="90" y1="80" x2="50" y2="10" style="stroke:rgb(255,0,0);stroke-width:1"/>

    <circle cx="170" cy="80" r="2" fill="blue"/>
    <circle cx="130" cy="150" r="2" fill="blue"/>
    <line x1="90" y1="80" x2="130" y2="150" style="stroke:rgb(0,0,255);stroke-width:1"/>
    <line x1="130" y1="150" x2="170" y2="80" style="stroke:rgb(0,0,255);stroke-width:1"/>
</svg>

效果:

image-20211111224320542

注意:T命令前面必須是一個Q命令,或者是另一個T命令,才能達到這種效果。如果T單獨使用,那麼控制點就會被認為和終點是同一個點,所以畫出來的將是一條直線。

A(elliptical Arc)弧形

A命令用於畫弧形。

A rx ry x-axis-rotation large-arc-flag sweep-flag x y
a rx ry x-axis-rotation large-arc-flag sweep-flag dx dy

引數說明

  • 弧形命令A前兩個引數rx和ry分別是x軸半徑和y軸半徑。
  • 弧形命令A的第三個參數列示弧形的旋轉情況。
  • large-arc-flag決定弧線是大於還是小於180度,0表示小角度弧,1表示大角度弧。
  • sweep-flag表示弧線的方向,0表示從起點到終點沿逆時針畫弧,1表示從起點到終點沿順時針畫弧。
  • x:結束點x座標。
  • y:結束點y座標。

示例:

<svg width="400px" height="320px" version="1.1" xmlns="http://www.w3.org/2000/svg">
  <path d="M10 315
           L 110 215
           A 30 50 0 0 1 162.55 162.45
           L 172.55 152.45
           A 30 50 -45 0 1 215.1 109.9
           L 315 10" stroke="black" fill="red" stroke-width="2" fill-opacity="0.5"/>
</svg>

效果:

image-20211111230425272

程式碼解析:

畫布上有一條對角線,中間有兩個橢圓弧被對角線切開(x radius = 30, y radius = 50)。

第一個橢圓弧的x-axis-rotation(x軸旋轉角度)是0,所以弧形所在的橢圓是正置的(沒有傾斜)。

在第二個橢圓弧中,x-axis-rotation設定為-45,所以這是一個旋轉了45度的橢圓,並以短軸為分割線,形成了兩個對稱的弧形。

總結

學習了直線命令,特別是比較難理解的曲線命令。

繪製平滑曲線的命令有三個,其中兩個用來繪製貝塞爾曲線,另外一個用來繪製弧形或者說是圓的一部分。
在path元素裡,只存在兩種貝塞爾曲線:三次貝塞爾曲線C,和二次貝塞爾曲線Q。

在這裡還要注意簡寫的貝塞爾曲線使用的前提。最難的當然是弧形了。

當然看到這裡,有朋友可能會有疑問,還有必要去學習這個複雜SVG的畫法嗎?對於這個問題,其實寫這篇文章的目的不是讓你手寫SVG,而是理解SVG的畫法,SVG大多數都是一些向量圖形工具來製作,但是細心的你沒發現嗎,上文的一些操作,不正是應和了我們使用AI等工具繪圖的場景嗎?

學習終是在腳下,累積也是如此。

感謝

萬能的網路

路徑-SVG|MDN

以及勤勞的自己,個人部落格GitHub測試GitHub

公眾號-歸子莫,小程式-小歸部落格

相關文章