cubic-bezier 曲線是 css3 動畫的一個重要基石。另一個爲 steps (ease 等都是 cubic-bezier 的特殊形式),css3 中的 cubic_bezier 曲線限制了首尾兩控制點的位置,通過調整中間兩控制點的位置可以靈活得到常用的動畫效果,同時 canvas 也進行了相應的支持,也存在相應的工具可以根據想要的曲線得到對應 cubic bezier 曲線的控制點參數。
而 ie(6-9) 卻沒有相應的支持,爲了能在各個平臺得到一致的動畫效果,則不可避免要在 ie 上通過定時器沿着指定控制點參數的 cubic bezier 曲線來手動更新動畫對象的數值.
模擬實現
公式
cubic-bezier 公式不是簡單的 y= x 公式,而是引入了第三個變量 t,由於動畫中關鍵在於計算比例,即在總時間的某個時間點百分比得到相應的值相對於最終值的比例,那麼只需要得到 0,1 區間的曲線即可。 而 [x,y] -> [0,1] 內的曲線則是通過 t 在 0,1 內連續變化而得到:
其中 P0, P1 ,P2, P3 都爲兩維 xy 向量
將向量拆開表示即爲:
y= (1-t)^3*p0y + 3*(1-t)^2*t*p1y + 3*(1-t)*t^2p2y + t^3p3y
x= (1-t)^3*p0x + 3*(1-t)^2*t*p1x + 3*(1-t)*t^2p2x + t^3p3x
而 css3 所用的 cubic bezier 已經限定死 p0 = (0,0) , p3= (1,1) ,因此公式可簡化爲
var ax = 3 * p1x - 3 * p2x + 1,
bx = 3 * p2x - 6 * p1x,
cx = 3 * p1x;
var ay = 3 * p1y - 3 * p2y + 1,
by = 3 * p2y - 6 * p1y,
cy = 3 * p1y;
y= ((ay * t + by) * t + cy ) * t
x= ((ax * t + bx) * t + cx ) * t
爲了提高效率以及減少計算精度丟失公式進一步經過了 Horner 's method 變化。
計算
css3 中某個限定了特定控制參數的 cubic -bezier 曲線如下所示:
動畫所做的事情就是把 x 軸當做時間比例,根據曲線得到 y 軸對應的值,並更新到動畫對象中去.
即轉化爲以下問題:如何根據上述公式在已知 x 的情況下如何得到 y.
求 t
首先需要根據公式
var ax = 3 * p1x - 3 * p2x + 1,
bx = 3 * p2x - 6 * p1x,
cx = 3 * p1x;
x= ((ax * t + bx) * t + cx ) * t
在已知 x 的情況下求 t,即經典的多項式求參問題,首先可以通過 newton method 嘗試求出 t 的值,若不行(可能性很小)則可通過可靠但慢速的二分法求值.
求 y
上步得到 t 後則可以帶入另一個 y 公式求得最終值 y
var ay = 3 * p1y - 3 * p2y + 1,
by = 3 * p2y - 6 * p1y,
cy = 3 * p1y;
y= ((ay * t + by) * t + cy ) * t
上述解法也是源自 webkit webcore c++ 原生實現.
使用對比
在傳入動畫的 easing 設置時,可以傳入 css3 cubic-bezier 的語法格式或者直接傳入特定的曲線設置(ease-in ease-out).
$('#xx').animate({ left:500 },{ duration: 2, easing: 'cubic-bezier(1,0.22,0,0.84)' // 'ease-in' });
效果對比:
通過對比即可發現,ease-out 和以前 js 實現的簡單二次曲線 easeOut 還是有明顯的不同,並且 js 實現和 css3 原生幾乎效果完全一樣(效率可能稍微低了些),更多自帶曲線對比可見: