在JS开发中经常会用到定时器,尤其是一些动画特效,小游戏等完全依靠定时器驱动。
要让动画跑得更流畅,我们常常使用较高的刷新率,例如60fps。由于每一帧的间隔非常短,很难看清楚每一帧具体的运行情况。
有时整体上看似乎一切良好,但如果放慢定时器的速度,却会发现其中有部分帧或因代码里的小问题,并没有按我们想象那样显示。由于播放的非常快,这些潜在的小问题都掩盖了。
为了方便动画脚本的观察和调试,我们尝试用js模仿一个类似windows下经典的应用程序:变速齿轮,能即时修改系统时钟的运行频率!这听起来似乎很神奇吧~其实原理并不复杂。
和传统的变速齿轮运行机制一样,我们使用钩子程序勾住默认那几个定时器相关的API —— setTimeout, setInterval, clearTimeout, clearInterval。当然,所谓的钩子程序,无非就是预先保存原来的API引用,接着用自己的程序覆盖他们。这样我们就可以拦截之后的定时器调用,然后根据虚拟时钟的速率,自己维护回调队列,于是就产生了定时器变速的效果。如果同步上再严密点,甚至还可以覆盖Date对象,让时间的流逝速度都随之而变!
当然,有不少问题仍然难以解决的。
时钟频率被加快后,每一帧的时间间隔被大幅缩短,甚至小于1ms。而js定时器频率众所周知,远达不到这样的精度。于是只能用“跳帧”来解决这个问题。例如,一个2ms的定时器,理论上应该每2ms触发一次,但某个浏览器最短只支持16ms的间隔,那么每次定时器触发内循环调用 16/2 = 8次回调,来弥补频率上的不足。但由于这8次回调是在同个事件里一口气执行的,所以前7次的界面操作都不会立即渲染出来,只有第8次的操作才会有所表现,所以就有了“跳”的感觉。
但是这个方法只能解决 setInterval 的定时器,因为它的时间间隔以及回调都是固定的。对于 setTimeout 这样每次延时不确定的,甚至还无法确定下一帧是否接着运行,跳帧机制就有些无从为力了。
不过只实现一个基本功能的还是比较容易的:
JsGear,就是根据这个思路实现的 JavaScript 变速齿轮插件,对时间相关的API都进行了严密的封装。因为它完全是用js/css制作的,所以不依赖任何插件,引入页面就可以使用。
为了让使用更快捷,这两天换了一个新概念的操作界面,并且支持IE6+, FireFox, Chrome, Safari, Opera浏览器,以及Quirks模式。
测试Demo: https://www.etherdream.com/JSGear/demo.html
在自己的页面里测试很简单,把jsgear.js插入到所有的脚本之前就可以。这里上传了一份到博客附件里,复制下面代码到自己的页面内即可使用:
当然,目前还只是演示版本,实现了基本功能,还有不少小问题。大家要是有什么好多思路和建议,欢迎探讨。