如何使用Tween.js各類原生動畫運動緩動算法
一、速戰速決
昨天有事回了趟江蘇,一來一回還堵車,1天基本上就在路上了,下午又當司機送夫人去買東西,周末工作時間嚴重不足,原本要早產的文章估計又要晚產了,為了爭取2點前寫完,我就盡量少廢話了。
二、關于Tween.js
Tween.js是一個包含各種經典動畫算法的JS資源,之前在多篇文章有提到過,例如之前寫的“ JavaScript與元素間的拋物線軌跡運動 ”,AS中甚至有專門的Tween類。
我自己悄悄瞅了一看,就一個JS文件,連個描述都沒有的的項目居然有147個star,看來今年用力一把,上500 star指日可待。
代碼截圖如下:
Quad , Cubic 等等都是經典的動畫運動算法名稱,完整列表如下:
- Linear :線性勻速運動效果;
- Quadratic :二次方的緩動(t^2);
- Cubic :三次方的緩動(t^3);
- Quartic :四次方的緩動(t^4);
- Quintic :五次方的緩動(t^5);
- Sinusoidal :正弦曲線的緩動(sin(t));
- Exponential :指數曲線的緩動(2^t);
- Circular :圓形曲線的緩動(sqrt(1-t^2));
- Elastic :指數衰減的正弦曲線緩動;
- Back :超過范圍的三次方緩動((s+1)*t^3 – s*t^2);
- Bounce :指數衰減的反彈緩動。
每個效果都分三個緩動方式,分別是:
- easeIn :從0開始加速的緩動,也就是先慢后快;
- easeOut :減速到0的緩動,也就是先快后慢;
- easeInOut :前半段從0開始加速,后半段減速到0的緩動。
很多小伙伴 easeIn 和 easeOut 哪個先快,哪個先慢一直記不清楚,我這里再給大家傳授一遍我獨門的邪惡記法,想想我們第一次OOXX,是不是進去( easeIn )的時候都是先慢,等進去了就快了;然后出來( easeOut )的時候,開始很快,都要出來了戀戀不舍速度就慢了。跟我們這里的動畫效果是完全匹配的。
所有的這些緩動算法都離不開下面4個參數, t , b , c , d ,含義如下:
/*
- t: current time(當前時間);
- b: beginning value(初始值);
- c: change in value(變化量);
- d: duration(持續時間)。
*/</code></pre>
只看上面字面意思其實不好理解,我們套用最簡單的線性勻速運動來解釋下:
Tween.Linear = function(t, b, c, d) {
return c*t/d + b;
}
比方說我們要從位置 0 的地方運動到 100 ,時間是 10 秒鐘,此時, b , c , d 三個參數就已經確認了, b 初始值就是 0 ,變化值 c 就是 100-0 就是 100 ,最終的時間就是 10 ,此時,只要給一個小于最終時間 10 的值, Tween.Linear 就會返回當前時間應該的坐標,例如,假設此時動畫進行到第5秒,也就是 t 為5,則得到(截圖自Chrome控制臺):

跟我們心中所想的值是一樣的,這就是這些緩動算法的運算原理。
對了,貌似 Elastic 和 Back 有其他可選參數,但我還沒時間去研究,所以,這里暫不做相關介紹。
三、如何實際使用Tween.js中的緩動算法?
上面示意的Tween.js中的線性勻速運動案例實際上只是某一個靜態數值,是無法構建動畫的,如果要想實現連續的具有明顯軌跡的動畫效果,我們需要不停地修改 t 的數值,一般來講都是一直往 d 的數值線性靠攏即可。
這里有個動詞“不停地修改”,換句話說就是不停地繪制,于是,想到了HTML5中的 requestAnimationFrame ,關于 requestAnimationFrame 我之前專門有文章介紹,如果瀏覽器不支持 requestAnimationFrame ,我們使用傳統的 setTimeout 定時器兼容實現即可。
// requestAnimationFrame的兼容處理
if (!window.requestAnimationFrame) {
requestAnimationFrame = function(fn) {
setTimeout(fn, 17);
};
}
因此,我們要顯示一個動畫效果,例如,還是拿上面的線性效果舉例,則代碼可以變成:
var t = 0, b = 0, c = 100, d = 10;
var step = function () {
// value就是當前的位置值
// 例如我們可以設置DOM.style.left = value + 'px'實現定位
var value = Tween.Linear(t, b, c, d);
t++;
if (t <= d) {
// 繼續運動
requestAnimationFrame(step);
} else {
// 動畫結束
}
};
基本上,所有的動畫使用都是這個套路。
然后,為了讓大家可以直觀體驗Tween.js中所有緩動算法的效果是怎樣的,我特意制作了一個包含完整效果的演示頁面,您可以狠狠地點擊這里: Tween.js動畫算法使用示意demo
點擊demo頁面顏色不太好看的小圓球,就會看到各自的運動速率和緩動狀態了,例如, Bounce.easeOut 的效果就是小球像皮球落地一樣彈幾下:

demo頁面上展示的源代碼就是處理后相當精簡的使用Tween.js的核心JS代碼,如果大家對完整的效果實現感興趣,可以右鍵頁面→查看頁面源代碼。
四、基于Tween.js更簡單調用的animation.js
Tween.js雖然原始且效果強大,但是,唯一的問題就是使用不方便,每次一個動畫我都要弄個 requestAnimationFrame ,而且4個參數有點多,不好記憶,順序什么的一旦弄錯就很麻煩,有沒有什么簡單的方法調用的,就像jQuery的 animation() 方法一樣。
出于這需求,我就手不停蹄弄出了一個更容易調用的animation.js,目前已經一起放在了 https://github.com/zhangxinxu/Tween 這個項目上,語法如下:
Math.animation(form, to, duration, easing, callback);
其中:
- form 和 to 是必須參數,表示動畫起始數值和結束數值;
- duration , easing , callback 理論上都是可選參數,但是實際上 callback 肯定是要使用的,因為實時變化的數值就是通過 callback 返回的。然后, duration , easing , callback 這3個參數的順序是任意的。具體來講:
- duration 為動畫持續時間,默認 300 ,默認單位是毫秒,建議使用數值,例如 600 ,也支持帶單位,例如 600ms 或者 0.6s ;
- easing 為緩動的類型,字符串類型,源自Tween.js。例如: 'Linear' , 'Quad.easeIn' , 'Bounce.easeInOut' 等等,需要注意大小寫。 其中,默認值是 'Linear' ;
- callback 為回調函數,支持2個參數(value, isEnding),其中 value 表示實時變化的計算值, isEnding 是布爾值,表示動畫是否完全停止。
所以,如果我們使用 Math.animation() 方法實現上面的線性運動效果則是:
Math.animation(0, 100, 170, function (value) {
// value就是當前的位置值
});
是不是更容易理解和記憶了!
示意demo頁面也有animation.js使用示意,其代碼如下:
Math.animation(0, 800 - 42, function (value) {
ball.style.transform = 'translateX(' + value + 'px)';
}, 'Bounce.easeInOut', 600);
五、結束語
Tween.js的強大之處在于,其本質上是一個算法,也就是在任何地方其實都是可以使用的,比方說canvas中或者SVG動畫實現等等,可以很好彌補CSS3 animation 不太方便使用的場景,以及一些與動態位置打交道的交互效果,位置是不確定的,只能借助JS實現,此時配合動畫算法,各種效果實現都不在話下。
有了上面的 Math.animation() 方法,實現不要太簡單,且不依賴任何jQuery, Zepto之類的工具類JS,我們悄悄地實現,讓設計師和產品經理驚訝下,喜出望外一下,豈不美哉!
比方說返回頂部效果,雖然說,直接瞬間到頂部也能滿足功能,但是效果而言太干了,都如果我們主動加個動畫效果,豈不是可以好好裝逼一把,例如,在本文的demo演示頁面,滾動到合適位置,然后打開控制臺中粘貼下面JS代碼然后回車:
Math.animation(document.documentElement.scrollTop, 0, function (value) {
document.documentElement.scrollTop = value;
}, 'Quart.easeOut', 600);
就會發現頁面滾動條好像自帶了剎車平滑滾動到了頂部。
animation.js寫得相當匆忙,時間有限,也并未詳盡測試,因此如果在使用時候發現問題,歡迎及時反饋,也更加歡迎共同建設,項目地址是: https://github.com/zhangxinxu/Tween
比方說,增加loop循環控制之類的~
恩,就這些,還有13分鐘2點,寫個摘要差不多趕在計劃前完成,速度還算不錯。
來自:http://www.zhangxinxu.com/wordpress/2016/12/how-use-tween-js-animation-easing/