三、時間精度問題
首先我們必須知道出現時間精度問題的原因。
如果你對javascript單線程機制不太熟悉,或者對setInterval、setTimeOut函數執行機制不太理解,希望你能先看一下這篇文章:How JavaScript Timers Work [Copy]。然後你可以看一下這篇文章http://icolin.org/javascript/settimeout-bug-in-ie-with-winxp.html。第一篇文章可以協助你理解javascript單線程機制以及setInterval、setTimeOut函數的執行機制,第二篇文章中 iColin 童鞋已經對時間精度問題出現的情況作了詳細的描述,他和我們在這裡的問題是一樣的,所以我就不再複述。最後一定要看 Omiga 的 再談JavaScript時鐘中的16ms精度問題 這篇文章。
(希望你確實看了那三篇文章,這樣你才能更加容易的理解下面的內容)
那麼現在相信你對問題出現的原因已經理解的差不多了,我在這裡再做一下小結:
javascript動畫在瀏覽器中啟動並執行過程中會產生時間誤差,誤差由系統內容,瀏覽器環境,程式運算時間,以及其他人為原因造成(我們這裡排除人為錯誤導致的誤差)。那麼系統內容包括電腦硬體、電腦作業系統,瀏覽器環境包括瀏覽器核心、javascript解釋引擎等,程式運算時間雖然有時可以忽略,但程式卻可能由於javascript的單線程機製造成時間延遲。所以如果想把時間精確到毫秒,那是很有難度的,jQuery也考慮到了這個問題,所以她採用了訊息佇列機制來盡量的消除這種誤差,不過效果差強人意。因為程式本身的時間精度就很難控制,所以如果使用程式本身來控制時間精度,那就好像拿油滅火。。。因為有很多不可抗力因素和外力因素影響。所以我們暫且將動畫時間精確到秒。
這裡對時間精度問題的討論就先到這裡,如果你有更好的解決方案,非常非常希望你能給予指導,非常非常感謝。
在這裡我們順便順便解釋一下上次遺留的 (這裡為什麼是以6px/30ms的速度等下解釋) 這個問題。
首先要說一下Tween演算法,我是從 cloudgamer 的 http://www.cnblogs.com/cloudgamer/archive/2009/01/06/tween.html這篇文章中看到了Tween演算法,進而在 robertpenner 的《Flash_MX編程與創意實現》中看到的非常細緻的講解。網址: http://www.robertpenner.com/easing/ 。如果想搞Flash創意和動畫的朋友推薦看這本書,我看了一部分,講得不錯,網上對這本書的評論也很好,我這裡有PDF中文版的,需要的跟我聯絡。
在Flash中,動畫時間長度都是以幀為單位來計算的。每幀時間長度*幀數就是動畫總時間。在我們javascript中,我們的幀時間長度實際上就是setInterval、setTimeOut的第二個參數。而幀數就是
1: Math.prototype.linearTween = function( t, b, c, d){
2: return t*c/d + b;
3: }
上面linear演算法中的d和t(d表示總幀數,t表示已經經過的幀數)。
所以如果你就像最開始那樣單純的認為t是時間,可能你就會陷入像我一樣的痛苦的迷茫中,半天不能自拔。我們再來複習一下這段代碼。
1: $ = function( id ){ return typeof id == "string"?document.getElementById(id):id }
2:
3: /* des:tween演算法。
4: t: 動畫已經執行的時間(實際上時執行多少次/幀數)
5: b: 起始位置
6: c: 終止位置
7: d: 從起始位置到終止位置的經過時間(實際上時執行多少次/幀數)*/
8: tween = {
9: linear : function( t, b, c, d){
10: return t*c/d + b;
11: }
12: }
13:
14: move={
15: moveType : function(mvTp){return mvTp && typeof(mvTp)== ”string” && tween[mvTp] ? mvTp: "linear" },
16:
17: startMove : function( mvObj,mvTp,t,b,c,d ){
18: t ? t : t=0; b ? b : b = 0; c ? c : c =300; d ? d : d = 50;
19: $(mvObj).style.position =="relative" || $(mvObj).style.position =="absolute"
20: ? 1 : $(mvObj).style.position = "relative";
21: //每隔30毫秒重複執行改變元素位置的函數
22: mvTimer = setInterval(function(){
23: //判斷動畫已經執行的時間(次數/幀數)是否小於總時間,是的話繼續執行改變位置的函數,否則的話,清理該interval。
24: t <= d
25: ? function(){ $(mvObj).style.left = parseInt(tween[move.moveType(mvTp)](t,b,c,d))+"px"; t++;}()
26: : clearInterval(mvTimer);
27: } ,30 )
28: }
29: }
30:
31: move.startMove("moveLinear");
在這裡我們動畫的幀數是d=50,經過幀數為t,當t<=d時,繼續執行動畫,每幀的時間間隔是30毫秒,該元素將從距離視窗左邊距為0的位置移動到距離視窗左邊距為300的位置(這裡忽略body的預設margin)。
今天先到這裡,下次將會接著分析Tween演算法,我將盡最大努力來經曆這個有意思的東西。