技术分享 | 一条神奇的曲线——贝塞尔曲线在前端的应用
源寶導讀:在前端的開發中我們經常會遇到利用貝塞爾曲線幫助我們完成前端的動畫和圖形繪制,但是對其中的一些參數配置是一頭霧水。本文將從貝塞爾曲線的原理講起,由淺入深剖析一階到多階貝塞爾的實現原理,最后從三個方向來介紹它的實際應用。
一、IOS圖標莫名的舒適感
先來對比下面兩張圖:
如果你用過蘋果手機就都會有一種感覺,很多安卓手機的圖標都會像左側圖標這樣——倒角和直線的過渡處有些許不自然;而現在流行的IOS系統圖標都是右側的這種,圓潤的倒角,這種就是有一種說不出來哪里來的舒適感:蘋果的圖標為什么看起來就是比一般的安卓圖標要高級?
在IOS6以及以前的版本上,蘋果都是用的倒圓角這種圖標;但是2013年6月蘋果在IOS7發布會上,蘋果將iOS上標志性的圓角圖標輪廓作了更新,將工業設計中的曲線連續概念應用到了視覺設計之上,而這個曲率連續的線,就是我們今天要提到的——貝塞爾曲線。
二、歷史起源
貝塞爾曲線(Bézier curve),又稱貝茲曲線或貝濟埃曲線,是應用于二維圖形應用程序的數學曲線。貝塞爾曲線于1962年由法國工程師皮埃爾·貝塞爾(Pierre Bézier)所廣泛發表,他運用貝塞爾曲線來為汽車的主體進行設計。現在主流的曲線設計基本都會用到這條曲線;常用的矢量圖形軟件都會通過它來精確畫出曲線,比如ps的鋼筆工具等。
三、數學原理
這條線到底神器在哪?在日常開發中,我們常用的是二次貝塞爾曲線和三次貝塞爾曲線,二次貝塞爾曲線只需要用到三個點,即可繪制出一條的平滑曲率過度自然的曲線;而蘋果手機的邊框和圖標的倒角設計則用到了四個點,就完成了近乎完美的曲線倒角,接下來我們將深入的了解原理看看這條線是如何實現的。
1)向量:
在了解貝塞爾曲線之前,需要先了解一個定義向量:
向量指具有大小和方向的量,它可以形象化地表示為帶箭頭的線段,二維里面向量是一個[x,y] ,三維則是[x,y,z];
重要的事情說三次!下面定義里所有介紹的P0P1...Pn,ABC..都是向量!都是向量!都是向量!(目前只討論二維)
2)一階貝塞爾曲線和多階貝塞爾曲線:
下圖是一到四階的貝塞爾曲線的示意圖
① 一階:
其實很簡單,看上圖第一個圖形,點A 隨著時間t 在P0到P1上運動,所有點的集合就是一次貝塞爾曲線。
用向量的角度來看:
一次貝塞爾曲線 B(t),P0P1線段上隨著時間t的變化( 0≤ t ≤1 ), P0表示向量 [x0 ,y0] P1表示向量 [x1, y1], A的值B(t)即為公式:
A = P0 + (P1-P0)*t
A = (1-t)P0 + t*P1
B(t) = (1-t)P0 + t*P1
結論:一階貝塞爾曲線實際就是一條線段。
② 二階:
他的定義是這樣的:
由P0至P1的連續點A,描述一條線段;由P1至P2的連續點B,描述一條線段;由A至B的連續點C:B(t) 描述一條二次貝塞爾曲線。
那么這些點肯定遵守下面的規則,
t = (A - P0) / (P1 - P0) = ( B- P1) / (P2 - P1) =? ( C - A ) / (B - A)
最終的公式如下:
A = (1-t)P0 + t*P1
B = (1-t)P1 + t*P2
C = (1-t)A + t*B
C = (1-t)( (1-t)P0 + t*P1 )? + t * ( (1-t)P1 + t*P2 )
C = (1-t)2P0 + 2t(1-t)P1 + t2P2;0≤t≤1
B(t) = (1-t)2P0 + 2t(1-t)P1 + t2P2;0≤t≤1
結論:二階的貝塞爾通過在控制點之間再采點的方式實現降階, 每一次選點都是一次的降階。
③ 三階:
理解了二階以后,三階可以依葫蘆畫瓢很快得出
A = (1-t)P0 + t*P1
B = (1-t)P1 + t*P2
C = (1-t)P2 + t*P3
D = (1-t)A + t*B
E = (1-t)B + t*C
F = (1-t)D + t*E
F = ....
結論:這個不就是遞歸嘛?
總結:
兩個點對應一階的貝塞爾曲線,三個點對應二階,四個點對應三階;
每一次的計算都一個降階計算,高階的貝塞爾可以通過不停的遞歸直到一階。
n個控制點對應著n-1階的貝塞爾曲線,并且可以通過遞歸的方式來繪制。
3)公式
通用公式:
或者導數表示:
感興趣的同學可以下去研究一下,這里就不做過于深入的研究了。
四、貝塞爾曲線在前端的應用
貝塞爾曲線在web前端主要在三處會用到:分別是css,svg,canvas
1)???? svg
svg的path路徑d屬性,支持二次(二階)貝塞爾曲線和三次(三階)貝塞爾曲線,縮寫:
C和Q,例如:
Q 300 300, 500 100? 二階
C 200 200, 400 200,? 500 100 三階
我們先來看看一個demo,如下圖的曲線:
要實現上圖的效果,看下圖第二行代碼svg的path屬性當中 d的值:
M100 100 Q 300 300, 500 100? 實際就是對應的二次貝塞爾曲線的,三個點:
?第一個點:Q 前面的 100 100 起始點對應P0,
?第二個點:Q后面第一個點 300 300 對應控制點P1
?第三個點:Q后面的第二個點 500 100? 對應結束點 P2
依此類推:類ball02:
M100 100 C 200 200, 400 200, ?500 100? 就是對應的三次貝塞爾曲線的,四個點:
??第一個點:C 前面的M 100 100 起始點對應P0
??第二個點:C后面第一個點 200 200? 對應控制點P1
??第三個點:C后面第二個點 400 200? 對應結束點 P2
??第四個點:C后面第三個點 500 100? 對應結束點 P3
放代碼:
補充小知識點:
下面的命令可用于路徑path的d屬性和offset-path的path屬性:
M = moveto
L = lineto
H = horizontal lineto
V = vertical lineto
C = curveto
S = smooth curveto
Q = quadratic Belzier curve
T = smooth quadratic Belzier curveto
A = elliptical Arc
Z = closepath
屬于貝塞爾曲的有:
C S Q T
2)??? css3
① offset-path路徑
offset-path css的移動路徑? 可以用path的簡寫語法, 上面的svg的代碼已寫明
就是如下這種形式
offset-path: path('M100 100 Q 300 300, 500 100')
② cubic-bezier函數
函數?css3的cubic-bezier函數,主要就是在:
?animation-timing-function(設置動畫將如何完成一個周期)
?transition-timing-function, 屬性(設置動畫過渡的效果)當中使用
含義 cubic-bezier(x1,y1,x2,y2)函數具體含義如下,如下對應貝塞爾曲線的P0,P1,P2,P3
? P0:默認值 (0, 0)
? P1:動態取值 (x1, y1)
? P2:動態取值 (x2, y2)
? P3:默認值 (1, 1)
技巧?在css3中我們常用的幾個過渡效果其實都是用貝塞爾曲線的形式來表示的,這些方法名就叫緩動函數(文章末尾會附上緩動函數):
? ease: cubic-bezier(0.25, 0.1, 0.25, 1.0)
? linear: cubic-bezier(0.0, 0.0, 1.0, 1.0)
? ease-in: cubic-bezier(0.42, 0, 1.0, 1.0)
? ease-out: cubic-bezier(0, 0, 0.58, 1.0)
? ease-in-out: cubic-bezier(0.42, 0, 0.58, 1.0)
注意的點 :
css3的貝塞爾曲線只有2階和3階的,并且將貝塞爾曲線做了一個限制:除了所有參數必需,參數是數字值以外;x1 和 x2 需要是 0 到 1 的數字。比較常見transitio的動畫例如:寬度大小變化,顏色漸變,位置移動,內外邊距等,animation的話可以結合@keyframe 實現一些關鍵幀動畫,效果更好
Demo:
3)??? canvas
canvas比較簡單,語法如下:
1. 二階:quadraticCurveTo(x1,y1, x2,y2)
2. 三階:bezierCurveTo(x1,y1, x2,y2, x3,y3)
上面的二階,三階 和 svg的 C,Q 是一樣的語法,就不贅述了。
有一個波浪的效果這里全部是用canvas繪制的,代碼貼在文章末尾。
具體代碼:
分三塊:
1.dom區域:定義容器和尺寸,這個很好理解。
第二個區域:繪制外圈的圓形drawCircle和波浪效果 drawSin的方法。
第三個區域:繪制動畫,利用requestAnimationFrame方法不斷的重新繪制波浪效果(animation方法),不停的重復累加X軸橫向位置,這樣就可以實現一個不斷的波浪。
五、寫在結尾的話
貝塞爾曲線這條神器的曲線在前端的應用還是相當廣泛的,除了常見的css動畫,過渡效果,在svg和canvas的動畫當中也必不可少,掌握上述的三個方向,對面大部分的場景,前端的同學都能夠游刃有余的去應付。
文章如有紕漏可以在評論里指正交流。
文中用到的代碼:
鏈接: https://pan.baidu.com/s/1aMFK4Dehiz5KjddxN704vw? 密碼: b404
1. 曲線篇: 貝塞爾曲線??https://zhuanlan.zhihu.com/p/136647181
2. svg路徑繪制??http://svg.wxeditor.com/
3. 緩動函數?https://www.xuanfengge.com/easeing/easeing/
另外附上css3 cubic-bezier函數?緩動效果集合:
? $easeInSine: cubic-bezier(0.47, 0, 0.745, 0.715);
? $easeOutSine: cubic-bezier(0.39, 0.575, 0.565, 1);
? $easeInOutSine: cubic-bezier(0.445, 0.05, 0.55, 0.95);
? $easeInQuad: cubic-bezier(0.55, 0.085, 0.68, 0.53);
? $easeOutQuad: cubic-bezier(0.25, 0.46, 0.45, 0.94);
? $easeInOutQuad: cubic-bezier(0.455, 0.03, 0.515, 0.955);
? $easeInCubic: cubic-bezier(0.55, 0.055, 0.675, 0.19);
? $easeOutCubic: cubic-bezier(0.215, 0.61, 0.355, 1);
? $easeInOutCubic: cubic-bezier(0.645, 0.045, 0.355, 1);
? $easeInQuart: cubic-bezier(0.895, 0.03, 0.685, 0.22);
? $easeOutQuart: cubic-bezier(0.165, 0.84, 0.44, 1);
? $easeInOutQuart: cubic-bezier(0.77, 0, 0.175, 1);
? $easeInQuint: cubic-bezier(0.755, 0.05, 0.855, 0.06);
? $easeOutQuint: cubic-bezier(0.23, 1, 0.32, 1);
? $easeInOutQuint: cubic-bezier(0.86, 0, 0.07, 1);
? $easeInExpo: cubic-bezier(0.95, 0.05, 0.795, 0.035);
? $easeOutExpo: cubic-bezier(0.19, 1, 0.22, 1);
? $easeInOutExpo: cubic-bezier(1, 0, 0, 1);
? $easeInCirc: cubic-bezier(0.6, 0.04, 0.98, 0.335);
? $easeOutCirc: cubic-bezier(0.075, 0.82, 0.165, 1);
? $easeInOutCirc: cubic-bezier(0.785, 0.135, 0.15, 0.86);
? $easeInBack: cubic-bezier(0.6, -0.28, 0.735, 0.045);
? $easeOutBack: cubic-bezier(0.175, 0.885, 0.32, 1.275);
? $easeInOutBack: cubic-bezier(0.68, -0.55, 0.265, 1.55);
------ END ------
作者簡介
曹同學:?數據分析平臺的開發工程師,目前負責天際平臺相關開發工作。
也許您還想看:
技術分享|Java SDK動態數據源和上下文機制
技術分享|NodeJS分布式鏈路追蹤實現
技術分享 | Java SDK 元數據驅動的事件通信架構
技術分享|Hangfire深度實踐
技術分享 | APT結合JavaPoet生成模板化Java源代碼文件
技術分享 | 玩轉高效UI自動化測試
更多明源云·天際開放平臺場景案例與開發小知識,可以關注明源云天際開發者社區公眾號:
【建模】文檔服務提供高保真打印模式
明源云·天際硬核技術認可:獲華為鯤鵬技術認證書
天際·開發者社區“重裝發布”!
總結
以上是生活随笔為你收集整理的技术分享 | 一条神奇的曲线——贝塞尔曲线在前端的应用的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 无需Windbg | 使用VS 2019
- 下一篇: OAuth 2.1 的进化之路