深度掌握SVG路徑path的貝塞爾曲線指令

深度掌握SVG路徑path的貝塞爾曲線指令

一、數字、公式、函數、變量,哦,NO!

又又一次說起貝塞爾曲線(英語:Bézier curve,維基百科詳盡中文釋義戳這裏),我最近在嘗試實現複雜的矢量圖形動畫,發現對貝塞爾曲線的理解饅頭那麼厚,是完全不能承受富有創意的創作的,至少得有我當年追我老婆的臉皮厚才行。

然而,瞅瞅維基百科上的釋義,或者其他一些相關的技術文章,總是離不開各種公式,一大堆變量……例如下面維基截圖縮略圖:
維基百科上的公式與變量

完全是數學愛好者的菜啊!我想,要是讓UI設計師們去學習這些東西,估計還不如一刀來個痛快的!

這就是愛好領域與能力掌握的悖論!

從思維方式以及理論根基上來講,設計師更能做出細膩、靈動的動畫效果。在傳統動畫領域,往往都是美術人員做主導實現的,畢竟是需要手工一幅一幅繪製的(Flash動畫也需要大量繪製)。

然而,在web上,例如webGL, canvas或者SVG, 目前都還沒有什麼可視化的工具,可以讓設計師直接擺弄擺弄就能出現自己想要的效果。唯一的解決之道就是——coding.

於是問題來了,設計師滿滿一顆做動畫做效果的心,可惜碼代碼不是他們喜歡的菜;碼農雖然喜歡碼代碼,可以讓SVG等動起來,但是繪畫能力有限,圖形不敏感,動畫把控不到位。我本將心照明月,奈何明月太高上不去!正因爲這種無奈,所以纔有了細化的職位分工。策劃負責創意,設計師負責基礎圖形,腳本工程師浮動動起來。看上去很不錯,然而,腳本工程師能否全方位體現最初創意與設想就要打一個大大的問號了?

凡是銜接(尤其是有血有肉有思考有創造力的個體)必定會出現接縫。我小時候玩過一個遊戲,「悄悄話傳遞」,老師告訴第一個人的話可能是:“張含韻我喜歡你”,但是,傳到最後一個人可能是,“張含韻傻逼啊你”!

因此,本職紮實,其他上下游銜接也有所涉獵的員工能做出更犀利的產品。

不知道大家有沒有看過「QQ瀏覽器mac版」的宣傳頁,http://browser.qq.com/mac/, 使用Chrome等高級瀏覽器,效果很贊,同行們都讚不絕口!

QQ瀏覽器for Mac截圖縮略圖

不少人調侃,要是瀏覽器做的有這個頁面好那就好了! 

哈,小道消息,此頁面是從界面設計到效果實現完全是一個人完成的,一個懂CSS3的設計師做出來的產品。我懂技術,同時又是我自己的設計,我自然知道該怎麼設計才能做出最理想的效果,自然知道怎樣的技術可以實現我想要的設計。

然而,然而,注意,我要轉折了……

「QQ瀏覽器mac版」的宣傳頁的效果雖好,但是,細細一查看,會發現,所有燦爛的效果元素,全部都是png24圖片,然後CSS3 3D控制。

也就是數學的、邏輯的東西很少很少。在國外,HTML/CSS其實都是web設計師學習的必修課,很多優秀的頁面重構er都是設計師背景。這種分工其實更符合做出偏前的優秀作品。

我經常會問求職者,你覺得你脫穎而出的地方是哪裏?如果簡歷中只有顯示做了很多網站,我覺得這遠遠不夠!由於沒有很多人都討厭的,被高考拍死的數學邏輯,HTML/CSS的入門實際是很簡單(但這種簡單的意義非常深遠,以後再聊),門檻很低,效果又是所見即所得,容易培養興趣,所以茫茫中國,會寫頁面的人何其多。要想脫穎而出,要麼有萬人之上的深度,要麼萬人玩不來的技能

萬人之上的深度:勤奮刻苦會讓你有所成,但是,想要登峯造極那必須是要有天賦的。所以,單純想通過HTML/CSS走上人生巔峯,如果沒有足夠的天賦,以及超幸運的環境支持(公司寫代碼的全男的,就我一個女的,還美女),那麼你會很快撞到人生的天花板。
萬人不會的技能:在web領域,我覺得此技能爲兩塊:一是紮實的設計功力,二是數學功底與邏輯思維能力。二者其一即可(兼得的跟我們不是一個檔次的,不予討論)。小時候的薰陶、繪畫基本功練習,設計理論的學習,這些是很難被一般人超越的。所以,普通人想去轉行做設計,除了一些罕見的具有天賦的人,往往會拿激情當才能,最後苦逼命!第二個,就是數學功底與邏輯思維能力。恰好,今天微博上看到了一張圖:
高考數學忘光光

雖然有玩笑成分,但不可厚非,工作之後的數學功力是個很明顯的技能優勢。恰好,動畫,web矢量繪製需要用到大量的數學知識。於是,大家是不是看到了一個可以跟其他芸芸區分的方向?至於邏輯思維,更多涉及JavaScript以及後臺開發等相關知識,即計算機背景優勢,阿里的前端基本上都是這麼一出。

於是,我提煉了下可以脫穎而出的技能組合:

技能 程度 境況
設計 | HTML/CSS deep | good+ 巔峯
HTML/CSS good 要飯 
HTML/CSS deep 搬磚
HTML/CSS/JS | 數學、動畫、圖形 deep | good+ 巔峯
HTML/CSS/JS | 後臺語言、服務器 deep | good+ 巔峯
設計 | HTML/CSS/JS | 數學、動畫、圖形 | 後臺語言、服務器 good+ | deep | good+ | good+ 找不到對象

所以,要想前端有所成,有兩條路,一是往前,webGL, canvas, SVG領域,這需要對圖形敏感,有設計感,有動畫素養,有相當的數學知識,以及最重要的JavaScript控制能力;一是往後,走開發路線,工具,富應用,運維(數據統計、前端安全、前端部署)領域,這需要懂後臺、計算機網絡、邏輯思考能力,以及最重要的JavaScript開發功力。

無論那條路,JavaScript的掌握都是基礎、都是中樞。So, 雖然新技術,新方向很誘人,但是,如果基礎不好,走兩步就會摔倒,吃力不討好。靜下心來,好好紮實前端基礎再說,年輕當時,步子跨大了,當心扯着你未來老婆的最愛。

好了,方向是明確了,原因也知道了,該如何做呢?

要想進入前端圖形領域,身爲動畫基礎、路徑繪製基礎的貝塞爾曲線那必須要掌握透徹,爛熟於心啊!可是,可是……看到那比棒棒糖還長的數學公式、一堆還給老師的符號,我,我怕……
棒棒糖 張鑫旭-鑫空間-鑫生活

我其實也很怕,但是呢,貝塞爾曲線數學公式的意義在於,讓你知道,貝塞爾曲線是如何繪製的,屬於更底一層的學習。我們不是做SVG編輯器,不是做像Photoshop那樣的工具,因此,我們的學習可以再往上一層,關心貝塞爾曲線指令,讓瀏覽器去幫我們繪製此指令下的貝塞爾曲線。至於公式,可以在我們對貝塞爾曲線足夠熟悉之後,再去深入,那就是另外的文章了。

二、貝塞爾曲線基本概要

“貝塞爾曲線”是什麼?這個問題大可不必關心,只要知道,這廝可以繪製任何曲線,自然包括直線。

貝塞爾曲線不太好掌握的原因之一就是它的兄弟姐妹有點多:線性貝塞爾曲線二次方貝塞爾曲線三次方貝塞爾曲線四次方貝塞爾曲線五次方貝塞爾曲線、……。

維基百科上有很贊但不推薦細看的Gif動畫:

線性貝塞爾曲線演示動畫,t在[0,1]區間

線性貝塞爾曲線演示動畫,t在[0,1]區間

二次貝塞爾曲線演示動畫,t在[0,1]區間

二次貝塞爾曲線演示動畫,t在[0,1]區間

三次貝塞爾曲線演示動畫,t在[0,1]區間

三次貝塞爾曲線演示動畫,t在[0,1]區間

四次貝塞爾曲線演示動畫,t在[0,1]區間

四次貝塞爾曲線演示動畫,t在[0,1]區間

五次貝塞爾曲線演示動畫,t在[0,1]區間

五次貝塞爾曲線演示動畫,t在[0,1]區間

以上動圖都是基於數學公式,目前階段接觸公式不利於我們的學習,因此,大家感受一下就可以了。

三、SVG貝塞爾曲線指令概要

Canvas, 以及CSS3動畫函數也有貝塞爾曲線的,但他們的用法形式上與SVG是不同的(訪問該地址可大致感受其異同)。

SVG中path的元素,也就是路徑繪製,屬性名稱是d, 具體值是由專門的“指令字母+座標值”實現的,例如下面這個簡單代碼示意:

<path d="M10 10L90 90" stroke="#000000" style="stroke-width: 5px;"></path>

標準的指令字母是10個,外加1個非標準的,這個可以參見我翻譯構建的Snap.svg項目Paper.path()頁面中的表格:

命令 名稱 參數
M moveto  移動到 (x y)+
Z closepath  關閉路徑 (none)
L lineto  畫線到 (x y)+
H horizontal lineto  水平線到 x+
V vertical lineto  垂直線到 y+
C curveto  三次貝塞爾曲線到 (x1 y1 x2 y2 x y)+
S smooth curveto  光滑三次貝塞爾曲線到 (x2 y2 x y)+
Q quadratic Bézier curveto  二次貝塞爾曲線到 (x1 y1 x y)+
T smooth quadratic Bézier curveto  光滑二次貝塞爾曲線到 (x y)+
A elliptical arc  橢圓弧 (rx ry x-axis-rotation large-arc-flag sweep-flag x y)+
R Catmull-Rom curveto*  Catmull-Rom曲線 x1 y1 (x y)+

其中,Catmull-Rom曲線不是標準的SVG命令,我們這裏不予以討論!

如果指令字母是大寫的,例如M, 則表示座標位置是絕對位置;如果指令字母小寫的,例如m, 則表示座標位置是相對位置。//zxx: 本文全部使用大寫字母做演示和說明。

其中,有5個指定屬於基本指令,你也可以理解爲“好理解好上手好記憶”的指令,見下表:

指令字母(絕對座標) 中文含義 參數示意 具體說明
M 移動到(moveTo) x,y 路徑起始點座標
Z 閉合路徑(closepath)   將路徑的開始和結束點用直線連接
L 直線(lineTo) x,y 當前節點到指定(x,y)節點,直線連接
H 水平直線 x 保持當前點的y座標不變,x軸移動到x, 形成水平線
V 垂直直線 y 保持當前點的x座標不變,y軸移動到y, 形成垂直線

除了這5個參數少、直來直往的指令,剩下的,除了弧形命令A(Arcs),就都是與貝塞爾曲線相關的命令了。

弧形命令A雖然曲線更規律,但其由於不確定性(指定橢圓長半徑和短半徑,以及上面的兩個點的圓弧,可上半弧也可下半弧),因此,還需要額外的參數保證其弧線唯一,所以其比貝塞爾曲線看上去要複雜,不過大家不要怕,這裏我們不跟他打交道。
送了一口氣

剩下的4個指令就是繪製貝塞爾曲線相關的了。

他們分別是:C大仙,S大仙,Q大仙,T大仙。合稱“廁所切圖(CSQT)大仙”。其中,C大仙,S大仙是一夥的,一個組合,你知道叫神馬組合嗎?「廁所大仙組合!」哎呀,這位觀衆,你好生了得,前途無量,答得太對了,就是“廁所(CS)大仙組合”;Q大仙,T大仙是另外一夥的,你知道……「切圖大仙組合!」哎呀,這位觀衆,看來天空纔是你的極限,你已經學會搶答啦!沒錯,就是“切圖(QT)大仙組合”。

那這“廁所大仙組合”和“切圖大仙組合”是怎麼組合的呢?各自的模樣、身世又是如何的呢?且聽下回分解……

四、“廁所大仙組合”和“切圖大仙組合”

廁所大仙組合
“廁所大仙組合”是專門繪製三次貝塞爾曲線的。廁所,是個真實世界的物體,是三維的,因此,跟三次貝塞爾曲線的“三”正好對應。

那三次貝塞爾曲線的樣子是?

喏,這裏各種尺寸杯罩形狀都是“三次貝塞爾曲線”:
SVG與貝塞爾曲線

這種曲線,只用到了“廁所組合”中的“廁”,也就是指令C.

爲方便演示具體參數,我就拿上圖第一個A杯罩的放大舉例:
三次貝塞爾曲線的控制點等參數

上圖,是Adobe Photoshop中使用鋼筆工具時候的截圖再加工。所謂鋼筆工具摳圖實際上就是一個一個貝塞爾曲線連接的結果,各類圖形繪製軟件類似工具本質上都是貝塞爾曲線。

我們可以看到,上面圖片有4個點出現,曲線的兩個端點,以及兩個控制點,這就是典型的三次貝塞爾曲線。

杯罩曲線是我們實際存在的路徑(描邊就可見),而兩個控制點是看不見的(虛的),只是用來確定曲線的弧度等。

一般而言,“三次貝塞爾曲線”的指令是:

 C x1 y1, x2 y2, x y 

記住,兩個控制點寫在前面,後面是一個實點。跟“虛虛實實”這個詞前三個字對應,就很好記憶了!如果使用相座標置則是(後面不展示):

c dx1 dy1, dx2 dy2, dx dy

以上就是C大仙基本模樣了!

「擦卡馬尼亞!明明圖片上有4個點啊,你這裏的C指令怎麼就只有3個參數啊?」
「哦,這個啊。其中有一個點是起始點,一般是使用指令Mx, y的,這樣1+3就等於4啦~」

您可以狠狠地點擊這裏:C大仙指令下實現的三次貝塞爾曲線demo

或者直接往下瀏覽-逗號區分縱橫軸:

<svg id="svg" width="200" height="100">
    <desc>三次貝塞爾曲線</desc><defs></defs>
    <path d="M20,20 C90,40 130,40 180,20" stroke="#000000" fill="none" style="stroke-width: 2px;"></path>
    <text x="90" y="60">A杯罩</text>
</svg>

或者逗號用來區分每個點座標(主流寫法):

<svg id="svg" width="200" height="100">
    <desc>三次貝塞爾曲線</desc><defs></defs>
    <path d="M20 20 C90 40, 130 40, 180 20" stroke="#000000" fill="none" style="stroke-width: 2px;"></path>
    <text x="90" y="60">A杯罩</text>
</svg>

或者不需要逗號:

<svg id="svg" width="200" height="100">
    <desc>三次貝塞爾曲線</desc><defs></defs>
    <path d="M20 20 C90 40 130 40 180 20" stroke="#000000" fill="none" style="stroke-width: 2px;"></path>
    <text x="90" y="60">A杯罩</text>
</svg>

效果(IE9+, …):
A杯罩

那S大仙呢?

S大仙和C大仙是一對組合,職責之一就給C大仙補刀用的。

MDN上的這張圖的示意很贊:
S指令補刀示意

大家要把關注點放在藍線上面。S指令會自動補出一個對稱的控制點(藍線部分)。於是,就會有連續的平滑曲線啦!

C指令有三個座標參數,而S指令自動對稱一個控制點,因此,跟在C指令之後的S指令,只需要2個參數哦,如下:

S x2 y2, x y

C指令+S指令,就是“廁所大仙組合”啦!

但是呢,只要是組合,就會出現矛盾,比如說科比和奧尼爾,連白娘子和許仙都有不信任的時候。所以,S大仙前面的搭檔可能不是C大仙,而是S大仙,或者更苦逼的孤苦伶仃一個人。

S大仙生來補刀,無私,又簡化參數,很高大上,但同時又是個很有個性的人。

如果前面跟着的是其他S大仙,上陣不離兄弟兵,那就給前面的S大仙補刀;但是,如果前面即沒有C大仙,也沒有S大仙,孤苦伶仃一個人被拋棄,則S大仙就會叛變,自降一級,從三次貝塞爾到二次貝塞爾組合陣營,也就是“切圖大仙組合”,其表現就跟二次貝塞爾曲線的Q大仙一模一樣(兩個控制點合爲同一個點),彷彿在宣告:「讓你們無視我,我去跟Q大仙混!」

所以,親們,不要讓S大仙孤苦伶仃一個人哈~其不能一個人戰鬥,一個人戰鬥時候就變成其他陣營的了。

您可以狠狠地點擊這裏:C大仙+S大仙指令下的三次平滑貝塞爾曲線demo

相關HTML代碼如下:

<svg id="svg" width="200" height="100">
    <desc>三次貝塞爾平滑曲線</desc><defs></defs>
    <path d="M20 20 C90 40 130 40 180 20 S250 60 280 20" stroke="#000000" fill="none" style="stroke-width: 2px;"></path>
    <text x="90" y="60">A杯罩</text>
    <text x="230" y="60">贅肉小肚子</text>
</svg>

現代瀏覽器下效果如下:
A杯罩贅肉小肚子

切圖大仙組合
好巧啊,剛剛提到了“切圖(QT)大仙組合”中的“切(Q)大仙”。大家都知道,“切圖”是個二維平面的活,因此,“切圖大仙組合”是繪製二次貝塞爾曲線的。正好區別於:“廁所”是三維的,對應的是三次貝塞爾曲線。

QT大仙組合中的Q大仙對應CS組合中的C大仙,爲二次貝塞爾曲線的主攻手。

二次比三次少一次,自然就要更簡單點。

典型的Q大仙對應的二次貝塞爾曲線如下圖:
二次貝塞爾曲線

對應的指令和參數是:

Q x1 y1, x y

可以看到,跟三次貝塞爾曲線相比,就是2個控制點合爲1個,而孤零零的S大仙指令由於缺乏對稱控制點,只能2個控制點合爲1個,於是看上去就是叛逃到了Q大仙這裏。

下面說說T大仙,T大仙與S大仙是一路人,專門做平滑補刀,看MDN上示意圖,藍色標註是關鍵:
二次貝塞爾曲線T指令

自動補全對稱的控制點(上圖藍色部分),讓曲線平滑起來。與S大仙類似,前面需要是他的黃金搭檔Q大仙,或者是兄弟搭檔T大仙。如果你讓T大仙孤苦伶仃一個人,性格桀驁的T大仙就會叛逃,與S大仙類似,逃到下一級陣營,二次貝塞爾曲線的下一級,讓我想想,哦~~~好像是一次貝塞爾曲線,也就是直線啦!『沒有花香,沒有樹高,我是一個無人關心的小草』此時,只有這首歌才能唱出孤苦伶仃T大仙的悲哀了!

之所以T大仙會變成直線,是因爲T單獨使用的時候,其控制點就會被認爲和終點是同一個點,所以畫出來的是一條直線。

您可以狠狠地點擊這裏:Q大仙+T大仙指令下的二次平滑貝塞爾曲線demo

無逗號版HTML代碼示意:

<svg id="svg" width="300" height="100">
    <desc>二次貝塞爾平滑曲線</desc><defs></defs>
    <path d="M20 10 Q140 40 180 20 T280 30" stroke="#000000" fill="none" style="stroke-width: 2px;"></path>
    <text x="120" y="60">小蠻腰</text>
    <text x="200" y="40">小翹臀</text>
</svg>

支持SVG瀏覽器下效果:
小蠻腰小翹臀

四次貝塞爾曲線、五次……
萬變不離其宗,對於四次貝塞爾曲線、五次貝塞爾曲線、……,我們使用二次貝塞爾與三次貝塞爾曲線的完美組合就OK了,不必細述。

補充於2015-01-05
DAYU做了個任意二次、三次貝塞爾曲線呈現工具,我覺得挺實用的,這裏分享下:http://dayu.pw/svgcontrol/

五、通曉SVG路徑貝塞爾曲線指令的意義是?

這是個很好的問題,我們爲何需要熟練掌握SVG路徑貝塞爾曲線指令,是爲了繪圖?

我們有Adobe Illustrator, 有Google的SVG編輯器,我們可以直接所見即所得直接使用工具繪製,豈不比代碼來得速度?

言之鑿鑿,實際上,我們掌握SVG貝塞爾曲線指令,繪圖是小,控制繪圖爲大!

我們先看下面這個可愛的Gif跑步動畫:
CSS3跑步動畫

此圖來自我廠微信團隊白樹的移動web動畫設計的一點心得——css3實現跑步一文,矢量風格的圖片,可愛的Gif, 身爲職業病的我們一定會窺視下是如何實現的,喜歡這類Gif的會求教程。

到底是怎麼實現的呢?我不清楚原作者是怎麼實現的,不過白樹的實現方式一目瞭然,多個圖片幀+CSS3 動畫控制background-position實現的(下面兩張爲截圖)。

人物奔跑與Gif動畫
代碼截圖

請允許我“呵呵”一下,上面的實現方法屬於設計師思維模式方法(避免數學以及JavaScript),有幾個大問題:

  1. 成本:繪圖是個很非人力的活,連白樹都感嘆:

    用PS打開該大師的 GIF 圖,在時間軸窗口中有 24 張不同的圖片,通過一幀一幀的播放來實現跑步動畫,很簡單得說明做一個精細的動畫需要多費點心思和勞動力啊,向大師表示敬禮~

  2. 文件尺寸:爲了壓縮Sprite大小,白樹這裏只取了7張,但仍有37k, 37K啊,可以抵多少JS代碼啊,而且還不能Gzip.
  3. 動畫效果:7幀的動畫,呵呵,顯然,很難細膩到哪裏去,尤其速度慢的時候,使用代碼控制,可以精確到小數點後N位的像素值。
  4. 可控性:我想,藉助CSS3動畫,能控制的無非就是速度了。我想小妹妹上午紅衣服,下午藍衣服,估計只能另外搞一套圖了。

如果你是資深設計師,同時通曉上述方法,贊!好厲害的設計師。如果你是重構,通曉上面的方法,鄙人總覺得只是職業釣魚者又釣了一條魚,還不足以讓公司爲你加薪。但是,如果你是重構,使用前端開發的思維方式去處理,通過coding解決問題,嘖嘖,不得了,秒殺茫茫多重構啊!歸結爲:偏前的前端使用偏後的前端的技術解決偏前的問題獲得更多的錢,而非偏前的前端使用偏前的技術解決偏前的問題!

例如這裏,有沒有想過使用SVG繪製此圖,特定時間改變特定貝塞爾曲線值,重繪形成動畫效果。好處不必多說:兼容Retina, 文件尺寸小的一塌糊塗,IE9瀏覽器也兼容(CSS3 animation只能IE10+),而且動畫非常細膩,可控性非常高!只能用一個網絡詞形容——鳥炸天!

基本原理是,這種矢量風格的圖形,就是由一些貝賽爾曲線繪製的路徑填充色組合而來,例如那個細細的小腿,兩個三次貝塞爾曲線(僅位置差別)+一個二次貝塞爾曲線(足底)就可以完成。我們要控制腿動,只要更改特定時間,貝塞爾曲線參數指就可以了。一般,這種有規律的運動是由數學函數的,非常好控制。就算運動無規律,找幾個關鍵點做位移,也比一幀一幀繪製高效好多倍!然後,讓SVG重新繪製,哇哦~動畫就出現了。由於是SVG,我們可以很隨意控制顏色,腦袋的位置,甚至可以與用戶互動。例如,用戶鼠標在哪裏小妹妹就往哪個方向跑。

相比之下,圖片幀+CSS3動畫實現是不是弱爆了!偏開發的前端往往缺少設計的靈性、缺了點感性思維,雖然有做動畫的程序功力,但,天資決定了動畫的效果往往生硬;設計師雖然天生動畫好手,也很感興趣,然數學、腳本是個很深的鴻溝。此時,必須要遊走於設計與開發的純正前端出手了,這是我們的挑戰,也是我們的機遇,不做出點東西來,單靠嘴皮子是無法提高話語權的。

很多人肯定想看我所說的SVG+JS控制曲線的動畫效果。正在進行中……回頭,一定會展示一些SVG驅動的可愛動畫效果的。

六、結束語

圖形動畫領域很多東西都已一脈相承,一通百通的。掌握SVG的貝塞爾曲線,肯定對於CSS3動畫,Canvas的處理有很大幫助。動畫算法也都是一致,圖像處理算法也都是一致的。

好就這些,開篇已經吐槽夠多了,歡迎糾正文中表述不準確的地方,歡迎溝通,歡迎交流。 

參考文章:

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章