开发者社区> 异步社区> 正文
阿里云
为了无法计算的价值
打开APP
阿里云APP内打开

《JavaScript高效图形编程(修订版)》——2.3 定时器、速度和帧速率

简介: bouncyBoss对象现在要创建一个目标FPS为40的timeInfo实例(存在timer变量中)。moveAll()在每个迭代调用timeInfo.getInfo()得到时间系数,并将其传给每个bouncySprite实例的moveAndDraw()方法。
+关注继续查看

本节书摘来自异步社区《JavaScript高效图形编程(修订版)》一书中的第2章,第2.3节,作者:【美】Raffaele Cecco著,更多章节内容可以访问云栖社区“异步社区”公众号查看

2.3 定时器、速度和帧速率

本节将讨论如何在JavaScript中控制图形的更新速度以保证用户体验。我们想要图形有平滑流畅的移动,不要太快也不要太慢。用户计算机的性能会影响图形更新的速度。下面我们将讨论减少不同机器上速度差异的解决方案。

2.3.1 使用setInterval和setTimeout
JavaScript的setInterval()和setTimeout()函数使你可以定期调用JavaScript代码。需要定期更新图形的应用,比如电脑游戏,几乎都离不开这些函数。

你可以将回调函数传给setInterval()来重复调用此函数:

screenshot

注意执行bigFunction()花费20毫秒。如果循环间隔比这个值小呢?

screenshot

看起来20毫秒的bigFunction()会在第一个回调函数返回之前被重新调用。实际上,新的回调将排在队列中直到前面的回调函数结束。

如果延时更短点呢?

screenshot

可以预计每执行一个回调函数,若干个回调函数在排队。事实上,通常的行为是只有一个排队的bigFunciton()会激活。排队的回调函数会在第一个回调函数结束后立即执行吗?有可能,但不一定。其他时间和浏览器中运行的代码可能使得setInterval()回调函数被延时或丢弃。回调函数甚至有可能连续发生(比规定的间隔短),如果JavaScript发现一个时间窗口,可以清除队列。

这里想要说明的是:不能保证回调函数以指定的间隔执行。

setTimeout()在指定的延时后调用一个函数,和setInterval()类似,但可预见性更强。

screenshot

这会在50毫秒后,调用一次bigFunction()。和setInterval()一样,这个延时仅仅是一个参考。

你可以用setTimeout()连续调用一个函数,其行为将比setInterval()更可预见:

screenshot

每当bigFunction()结束,它设置另一个以自己作为回调函数的setTimeout()。

在这个例子中,尽管设置的timeout值比bigFunction()执行时间要短,回调函数只会在bigFunction()结束后再执行。实际上,执行的频率和下面使用setInterval()的代码类似:

screenshot

2.3.2 定时器精度
Windows下的浏览器只有粗粒度的定时器。例如Windows XP的底层操作系统定时器提供15毫秒精度。这意味着Date()、setInterval()和setTimeout()等JavaScript函数不能提供可靠的15毫秒以下的定时。Google Chrome是例外之一,它将Windows切换到一个准确的定时器模式并提供1毫秒的精度。

这里的要点是一个应用程序不应该依赖低于15毫秒(约1/64秒)的定时器。这个问题严重吗?一般情况下不严重。浏览器中不太可能或不应该运行对时间这么敏感的应用程序。动画也许会比预计的慢一点或快一点,游戏等应用程序中帧率也不是绝对的稳定。如果在一段时间内细致检查这些不精确的累加效果,也许可以看到一定的误差。不过,在通常情况下,比如玩游戏或看菜单特效时,这些误差是察觉不到的。

不过在使用Date()进行代码性能分析时要小心。下面的例子中,如果执行的代码太快结束的话,将得到不准确的结果:

screenshot

一个更好的解决方案是在较长的时间段(如1秒)内循环执行代码,然后用期间完成的迭代次数来衡量执行速度。

2.3.3 保持速度一致
前面的sprite实现,具体来说是移动sprite的代码,存在一个问题——不同的浏览器下动画和移动的速度(即帧率)不一样。比如2.8GHz的PC、Opera或Google Chrome等浏览器可以在移动100个Sprite时轻松达到50FPS(每秒帧数),Firfox也许能有30FPS,而IE8也许只有25FPS。如果考虑不同的硬件,帧率的差异会更大。

这对装饰性的动画和特效不是大问题,但游戏等应用程序需要一致的移动速度来保证可玩性。

为在不同的软硬件环境中保持速度一致,必须在sprite移动和动画涉及的计算中考虑帧率的不同。具体来说,一个以30FPS每帧移动两个像素的sprite,和一个以60FPS每帧移动一个像素的sprite看起来速度一样。这两者之间的主要视觉区别是30FPS sprite的移动不如60FPS sprite的移动流畅。不过至少看起来他们是以同样的速度在屏幕上移动。

为此,必须计算一个时间系数,并在移动和动画代码中使用。表2-4显示了一个例子。

screenshot

很明显,时间系数=目标FPS/实际FPS。

为计算实际FPS,可以用JavaScript的Date对象记录当前时间(即开始时间,单位为毫秒位)。在执行所有应用逻辑后再记录时间(结束时间)。下面是代码:

screenshot

如果CPU负荷过重,帧率会很慢。以6FPS、每帧10像素在屏幕上移动的sprite看起来不平稳,肯定不适合游戏。表2-5列出了帧率和流畅度的对应关系。
screenshot

这不是说10FPS的低帧率毫无用处。对俄罗斯方块这样的游戏而言,这种帧率也许就可以接受了。

现在我们创建一个timeInfo对象,它将提供保持应用速度一致所需的所有功能。它接受一个goalFPS参数,即我们想要达到的目标FPS。如果达不到,函数将调整移动速度使其至少看起来达到了goalFPS。timeInfo对象中还提供了其他时间相关的信息。

下面的函数返回一个对象,其中包含getInfo()方法。getInfo()方法返回一个对象,其属性如表2-6所示。

screenshot

paused变量表明这是在应用程序开始或暂停后,getInfo()第一次被调用。它保证在经过一个很长的暂停之后,getInfo()传回的值是良性的,并且不会返回一个非常大的值。

screenshot

我们通过从上一次getInfo()中记录的oldTime,减去新时间,得到经过时间(elapsed time)。然后用经过时间来计算帧率。+new Date()语句等价于new Date(). getTime();:

screenshot

然后返回一些有用的信息属性,如表2-6所示。

screenshot

接着我们定义pause()方法,在暂停应用程序时都应调用此方法。

screenshot

现在,我们可以在原始的bouncySprite和bouncyBoss代码中使用timeInfo对象了:

screenshot

moveAndDraw方法现在接受时间系数作为参数。计算和原来相似,但使用了时间系数。changeImage()函数的参数应该是整数,但因为animIndex受时间系数影响,也许不是整数。为此,我们复制一个整数版的animIndex为animIndx2,并传入changeImage():
screenshot

bouncyBoss对象现在要创建一个目标FPS为40的timeInfo实例(存在timer变量中)。moveAll()在每个迭代调用timeInfo.getInfo()得到时间系数,并将其传给每个bouncySprite实例的moveAndDraw()方法。注意只需要一个timeInfo实例即可,因为每个bouncySprite实例可以使用同一个系数。

版权声明:本文内容由阿里云实名注册用户自发贡献,版权归原作者所有,阿里云开发者社区不拥有其著作权,亦不承担相应法律责任。具体规则请查看《阿里云开发者社区用户服务协议》和《阿里云开发者社区知识产权保护指引》。如果您发现本社区中有涉嫌抄袭的内容,填写侵权投诉表单进行举报,一经查实,本社区将立刻删除涉嫌侵权内容。

相关文章
Javascript定时器(三)——setTimeout(func, 0)
setTimeout(func, 0)可以使用在很多地方,拆分循环、模拟事件捕获、页面渲染等
39 0
剑指Offer——扑克牌中的顺子(JS实现)
剑指Offer——扑克牌中的顺子(JS实现)
121 0
剑指Offer——矩阵中的路径(JS实现)
剑指Offer——矩阵中的路径(JS实现)
125 0
剑指Offer——II.0~n-1中缺失的数字(JS实现)
剑指Offer——II.0~n-1中缺失的数字(JS实现)
71 0
剑指Offer——I.滑动窗口的最大值(JS实现)
剑指Offer——I.滑动窗口的最大值(JS实现)
104 0
剑指Offer——顺时针打印矩阵(JS实现)
剑指Offer——顺时针打印矩阵(JS实现)
191 0
[前端插件] js返回顶部 效果实现
需要 jQuery参考 http://www.jb51.net/article/48203.htmcss: #goTop{ position:absolute; display:none; width:50px; hei...
908 0
+关注
异步社区
异步社区(www.epubit.com)是人民邮电出版社旗下IT专业图书旗舰社区,也是国内领先的IT专业图书社区,致力于优质学习内容的出版和分享,实现了纸书电子书的同步上架,于2015年8月上线运营。公众号【异步图书】,每日赠送异步新书。
12049
文章
0
问答
文章排行榜
最热
最新
相关电子书
更多
低代码开发师(初级)实战教程
立即下载
阿里巴巴DevOps 最佳实践手册
立即下载
冬季实战营第三期:MySQL数据库进阶实战
立即下载