1、什麼是貝塞爾曲線
1962年,法國工程師皮埃爾·貝塞爾(Pierre Bézier),貝塞爾曲線來爲爲解決汽車的主體的設計問題而發明了貝塞爾曲線。現在,貝賽爾曲線是計算機圖形學中至關重要的一種曲線,它能過優雅地模擬人手繪畫出的線。它經過控制曲線上的點(起始點、終止點以及多個參考點)來創造、編輯圖形。其中起重要做用的是位於曲線中央的控制線。這條線是虛擬的,中間與貝塞爾曲線交叉,兩端是控制端點。移動兩端的端點時貝塞爾曲線改變曲線的曲率(彎曲的程度);移動中間點(也就是移動虛擬的控制線)時,貝塞爾曲線在起始點和終止點鎖定的狀況下作均勻移動。javascript
2、貝塞爾曲線的應用
-
貝賽爾曲線普遍應用於繪圖軟件中,例如Adobe PhotoShop、Adobe Flash。css
-
Android能夠經過自定義的view來實現貝塞爾曲線前端
-
ios則可使用UIBezierPath類來生成貝塞爾曲線java
-
前端,canvas bezierCurveTo,css animation-timing-function: cubic-bezier(x,x,x,x}都有關於貝賽爾曲線的一些應用ios
3、貝塞爾曲線公式及其分析
一次:git
二次:canvas
三次:性能
n次測試
可是公式中只是給出了點與點之間的關係,並無給出y與x座標的關係,爲此咱們須要對其進行分解,下面以三次貝塞爾曲線爲例子:動畫
4、貝塞爾曲線在動畫中的應用
如圖,X軸用來表示時間,Y軸用來表示動畫的完成度。因此貝塞爾曲線在動畫中的應用很簡單,每隔必定時間傳入當前動畫的執行時間佔總時間的比例而後就能夠得出動畫在此時刻完成的程度,最後只須要設置一下此時刻動畫的完成度就好了。因爲它的起始座標分別爲(0,0),(1,1)。
JS代碼實現
咱們已經求出了三次貝塞爾曲線x,y與t的關係,那麼就能夠根據這個關係經過JS實現
function UnitBezier(p1x,p1y,p2x,p2y) { this.cx = 3.0 * p1x; this.bx = 3.0 * (p2x - p1x) - this.cx; this.ax = 1.0 - this.cx -this.bx; this.cy = 3.0 * p1y; this.by = 3.0 * (p2y - p1y) - this.cy; this.ay = 1.0 - this.cy - this.by; } UnitBezier.prototype = { sampleCurveX : function(t) { return ((this.ax * t + this.bx) * t + this.cx) * t; }, sampleCurveY : function(t) { return ((this.ay * t + this.by) * t + this.cy) * t; } }
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
可能到這裏感受上,咱們已經實現了貝塞爾曲線的在動畫上的應用了,咱們看下這個demo.
然而咱們發現原始JS實現的動畫效果明顯不一樣於CSS3中的動畫效果。那麼該如何去模擬了?看下面的代碼:
function UnitBezier(p1x,p1y,p2x,p2y) { this.cx = 3.0 * p1x; this.bx = 3.0 * (p2x - p1x) - this.cx; this.ax = 1.0 - this.cx -this.bx; this.cy = 3.0 * p1y; this.by = 3.0 * (p2y - p1y) - this.cy; this.ay = 1.0 - this.cy - this.by; } UnitBezier.prototype = { sampleCurveX : function(t) { //貝賽爾曲線t時刻的座標點的X座標 return ((this.ax * t + this.bx) * t + this.cx) * t; }, sampleCurveY : function(t) { //貝賽爾曲線t時刻的座標點的y座標 return ((this.ay * t + this.by) * t + this.cy) * t; }, solve:function(t){ this.sampleCurveY(this.sampleCurveX(t)) } }
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
咱們再來測試一下看下效果
額,這下差異更大。可是仔細想一想這裏全部的數據基本都是浮點運算,那麼產生的偏差可想而知。
5、浮點型運算偏差
在計算機中浮點型運算形成的偏差很是廣泛,不論是C、C++、java、javaScript等待語言都存在,由於都是按照IEEE 754標準來進行浮點運算。
IEEE 754
按照IEEE 754 標準,32位中有:
- 1位是符號位(sign)
- 8位是指數位(exponent)
- 23位是數值 (fraction)
那麼計算出的結果爲:
舉些栗子
好比 0.1的單精度浮點數在計算機二進制數爲:
0 01111011 10011001100110011001101
那麼實際值爲
好比 0.5的單精度浮點數在計算機二進制數爲:
0 01111110 00000000000000000000000
6、怎樣縮小浮點型運算產生的偏差
首先咱們能夠來看下最簡單的減小偏差的方法
放大法
因爲浮點型運算會產生偏差,所以咱們能夠變相地將它放大,知道它爲整數,最後返回的時候在將值還原,而這種方法是爲徹底將偏差消除的。
Math.formatFloat = function(f, digit) {
var m = Math.pow(10, digit);
return parseInt(f*m,10) m;
}
var numA = 0.1;
var numB = 0.2;
alert(Math.formatFloat(numA+numB,1)===0.3);
雖然這種方法在JS中可以很好地消除偏差,可是並非全部的場景均適應。它比較時候簡單的加減運算和位數較低的乘法除法運算,這是由於在JS中精確整數的範圍被定義爲:−9007199254740992到9007199254740992(2的53次方),也就是說最多16位。然而咱們的貝塞爾曲線應用在動畫的過程當中確定會產生位數超過16位的浮點數,這種方式仍然不能解決問題。
二分法
二分法是咱們在高中的時候老師就已經給咱們講過,不過那個時候一般是用來判斷f(x0)是正數仍是負數,有時候還會給出精度要求。在這裏咱們一樣也可使用二分法來解決,咱們只須要不斷地利用二分法找出(f(x0)-x)
function division(x,t1,t2, epsilon,func) {//計算x的近似值 var t0, t1, t2, x2, i; t2 = x; if (t2 < t0) return t0; if (t2 > t1) return t1; while (t0 < t1) { x2 = func(t2); if (Math.abs(x2 - x) < epsilon) return t2; if (x > x2) t0 = t2; else t1 = t2; t2 = (t1 - t0) * .5 + t0; } return t2; }
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
牛頓迭代法
牛頓迭代法也是經常在計算機中減小偏差的一種經常使用方法,公式以下:
設r是f(x)=0的根,選取x0做爲r的初始近似值,過點(x0,f(x0))作曲線 的切線L,L的方程爲y=f(x0)+f’(x0)(x-x0),求出L與x軸交點的橫座標 x1=x0-f(x0)/f’(x0) ,稱x1爲r的一次近似值。過點 作曲線 的切線,並求該切線與x軸交點的橫座標 ,稱 爲r的二次近似值。重複以上過程,得r的近似值序列,其中,xn+1=xn-f(xn)/f’(xn)稱爲r的n+1次近似值。
在處理貝塞爾曲線的過程當中,因爲咱們不可能求出真正準確的值,爲此咱們只須要知足f(x0)-x
最終的效果
最後已經很是接近CSS3自帶的貝塞爾曲線動畫效果了,雖然仍有一點差異,這是由於採起的方法只能使偏差只能縮小而不能被消除,並且精度越多,那麼JS動畫性能就越差,因此這裏精度爲設定爲0.01.