在JS開發(fā)中經(jīng)常會(huì)用到定時(shí)器,尤其是一些動(dòng)畫特效,小游戲等完全依靠定時(shí)器驅(qū)動(dòng)。
要讓動(dòng)畫跑得更流暢,我們常常使用較高的刷新率,例如60fps。由于每一幀的間隔非常短,很難看清楚每一幀具體的運(yùn)行情況。
有時(shí)整體上看似乎一切良好,但如果放慢定時(shí)器的速度,卻會(huì)發(fā)現(xiàn)其中有部分幀或因代碼里的小問題,并沒有按我們想象那樣顯示。由于播放的非?欤@些潛在的小問題都掩蓋了。
為了方便動(dòng)畫腳本的觀察和調(diào)試,我們嘗試用js模仿一個(gè)類似windows下經(jīng)典的應(yīng)用程序:變速齒輪,能即時(shí)修改系統(tǒng)時(shí)鐘的運(yùn)行頻率!這聽起來似乎很神奇吧~其實(shí)原理并不復(fù)雜。
和傳統(tǒng)的變速齒輪運(yùn)行機(jī)制一樣,我們使用鉤子程序勾住默認(rèn)那幾個(gè)定時(shí)器相關(guān)的API —— setTimeout, setInterval, clearTimeout, clearInterval。當(dāng)然,所謂的鉤子程序,無非就是預(yù)先保存原來的API引用,接著用自己的程序覆蓋他們。這樣我們就可以攔截之后的定時(shí)器調(diào)用,然后根據(jù)虛擬時(shí)鐘的速率,自己維護(hù)回調(diào)隊(duì)列,于是就產(chǎn)生了定時(shí)器變速的效果。如果同步上再嚴(yán)密點(diǎn),甚至還可以覆蓋Date對(duì)象,讓時(shí)間的流逝速度都隨之而變!
當(dāng)然,有不少問題仍然難以解決的。
時(shí)鐘頻率被加快后,每一幀的時(shí)間間隔被大幅縮短,甚至小于1ms。而js定時(shí)器頻率眾所周知,遠(yuǎn)達(dá)不到這樣的精度。于是只能用“跳幀”來解決這個(gè)問題。例如,一個(gè)2ms的定時(shí)器,理論上應(yīng)該每2ms觸發(fā)一次,但某個(gè)瀏覽器最短只支持16ms的間隔,那么每次定時(shí)器觸發(fā)內(nèi)循環(huán)調(diào)用 16/2 = 8次回調(diào),來彌補(bǔ)頻率上的不足。但由于這8次回調(diào)是在同個(gè)事件里一口氣執(zhí)行的,所以前7次的界面操作都不會(huì)立即渲染出來,只有第8次的操作才會(huì)有所表現(xiàn),所以就有了“跳”的感覺。
但是這個(gè)方法只能解決 setInterval 的定時(shí)器,因?yàn)樗臅r(shí)間間隔以及回調(diào)都是固定的。對(duì)于 setTimeout 這樣每次延時(shí)不確定的,甚至還無法確定下一幀是否接著運(yùn)行,跳幀機(jī)制就有些無從為力了。
不過只實(shí)現(xiàn)一個(gè)基本功能的還是比較容易的:
JsGear,就是根據(jù)這個(gè)思路實(shí)現(xiàn)的 JavaScript 變速齒輪插件,對(duì)時(shí)間相關(guān)的API都進(jìn)行了嚴(yán)密的封裝。因?yàn)樗耆怯胘s/css制作的,所以不依賴任何插件,引入頁面就可以使用。
為了讓使用更快捷,這兩天換了一個(gè)新概念的操作界面,并且支持IE6+, FireFox, Chrome, Safari, Opera瀏覽器,以及Quirks模式。
測(cè)試Demo: http://www.etherdream.com/JSGear/demo.html
在自己的頁面里測(cè)試很簡(jiǎn)單,把jsgear.js插入到所有的腳本之前就可以。這里上傳了一份到博客附件里,復(fù)制下面代碼到自己的頁面內(nèi)即可使用:
<scrpt src="http://files.cnblogs.com/jsapp/jsgear.js"></script>
當(dāng)然,目前還只是演示版本,實(shí)現(xiàn)了基本功能,還有不少小問題。大家要是有什么好多思路和建議,歡迎探討。