很久之前就對jQuery animate的實現非常感興趣,不過前段時間很忙,直到前幾天端午假期才有時間去研究。
jQuery.animate的每種動畫過渡效果都是透過easing函數來實現的。 jQuery1.4.2中就預置了兩個這樣的函數:
easing: { linear: function( p, n, firstNum, diff ) { return firstNum + diff * p; }, swing: function( p, n, firstNum, diff ) { return ((-Math.cos(p*Math.PI)/2) + 0.5) * diff + firstNum; } }
從參數名隱約可以推測出firstNum是初始值。要是你的數學學得比較好,你可以發現linear函數是直線方程式;如果你的物理學得比較好,你可以發現它是勻速運動的位移方程式(我數學和物理都沒學好,是別人提醒我的……)。那麼diff和p就是速度和時間了。
再看看jQuery.animate的原型:
animate: function( prop, speed, easing, callback )
各參數的說明如下:
prop:一組包含作為動畫屬性和終值的樣式屬性和及其值的集合。
speed:動畫長度。
easing:要使用的擦除效果的名稱。
callback:動畫完成時執行的函數。
元素的目前樣式值(firstNum)可以獲取,動畫時長(p)就是duration,最終樣式值是prop。理論上說,動畫速度(diff)是可以算出來的。但這又必然需要另一個函數來運算。這樣做明顯是不明智的。再看看呼叫easing函數的相關程式碼(位於jQuery.fx.prototype.step中):
var t = now();
...
var n = t - this.startTime;
this.state = n / this.options.duration;
...
this.pos = jQuery.easing[specialEasing || defaultEasing](this.state, n, 0, 1, this.options.duration);
可以發現,p參數的值也就是this.state的值,從上下文得知它其實是動畫的時間進度。而firstNum和diff的參數值都是寫死的,分別是0和1。這下easing函數的秘密完全被解開,p、firstNum、diff都是百分率而非實際數值,easing函數的回傳值也就是位移的進度。 diff的值是1,也就是以1倍的速度運行動畫。算出位移進度後,透過「初始值 (最終值-初始值)×進度」就可以算出目前位移值:
this.now = this.start ((this.end - this.start) * this.pos);
透過setInterval每隔一定時間(jQuery中是13ms)進行一次位移運算,直到當前時間與初始時間的差值大於動畫時長,這就是jQuery.animate的執行過程。
按照常規思路,動畫的實作方式是這樣的:透過setInterval每隔一定時間為某個值增加特定數值,直到這個值達到限制值。這樣做的主要問題是,不同瀏覽器的運行速度不同,從而導致動畫速度有差異,一般是IE下比較慢,Firefox下比較快。而jQuery.animate是以目前時間來決定位移值,某個時刻的位移值總是固定的,因而動畫速度不會有差異。