javascript基础学习系列二百八十九:动画与 Canvas 图形

图形和动画已经日益成为浏览器中现代 Web 应用程序的必备功能,但实现起来仍然比较困难。视 觉上复杂的功能要求性能调优和硬件加速,不能拖慢浏览器。目前已经有一套日趋完善的 API 和工具可 以用来开发此类功能。
毋庸置疑,是 HTML5 最受欢迎的新特性。这个元素会占据一块页面区域,让 JavaScript 可以动态在上面绘制图片。最早是苹果公司提出并准备用在控制面板中的,随着其他浏览器 的迅速跟进,HTML5 将其纳入标准。目前所有主流浏览器都在某种程度上支持元素。
与浏览器环境中的其他部分一样,自身提供了一些 API,但并非所有浏览器都支持这些 API,其中包括支持基础绘图能力的 2D 上下文和被称为 WebGL 的 3D 上下文。支持的浏览器的最新版 本现在都支持 2D 上下文和 WebGL。

使用requestAnimationFrame

很长时间以来,计时器和定时执行都是 JavaScript 动画最先进的工具。虽然 CSS 过渡和动画方便了 Web 开发者实现某些动画,但 JavaScript 动画领域多年来进展甚微。Firefox 4 率先在浏览器中为 JavaScript 动画增加了一个名为 mozRequestAnimationFrame()方法的 API。这个方法会告诉浏览器要执行动画 了,于是浏览器可以通过最优方式确定重绘的时序。自从出现之后,这个 API 被广泛采用,现在作为 requestAnimationFrame()方法已经得到各大浏览器的支持。

早期定时动画

以前,在 JavaScript 中创建动画基本上就是使用 setInterval()来控制动画的执行。下面的例子展 示了使用 setInterval()的基本模式:

   (function() {
      function updateAnimations() {
doAnimation1(); doAnimation2(); // 其他任务
}
      setInterval(updateAnimations, 100);
    })();

动画与 Canvas 图形

作为一个小型动画库的标配,这个 updateAnimations()方法会周期性运行注册的动画任务,并 反映出每个任务的变化(例如,同时更新滚动新闻和进度条)。如果没有动画需要更新,则这个方法既 可以什么也不做,直接退出,也可以停止动画循环,等待其他需要更新的动画。
这种定时动画的问题在于无法准确知晓循环之间的延时。定时间隔必须足够短,这样才能让不同的 动画类型都能平滑顺畅,但又要足够长,以便产生浏览器可以渲染出来的变化。一般计算机显示器的屏 幕刷新率都是 60Hz,基本上意味着每秒需要重绘 60 次。大多数浏览器会限制重绘频率,使其不超出屏 幕的刷新率,这是因为超过刷新率,用户也感知不到。

因此,实现平滑动画最佳的重绘间隔为 1000 毫秒/60,大约 17 毫秒。以这个速度重绘可以实现最平 滑的动画,因为这已经是浏览器的极限了。如果同时运行多个动画,可能需要加以限流,以免 17 毫秒 的重绘间隔过快,导致动画过早运行完。

虽然使用 setInterval()的定时动画比使用多个 setTimeout()实现循环效率更高,但也不是没 有问题。无论 setInterval()还是 setTimeout()都是不能保证时间精度的。作为第二个参数的延时 只能保证何时会把代码添加到浏览器的任务队列,不能保证添加到队列就会立即运行。如果队列前面还 有其他任务,那么就要等这些任务执行完再执行。简单来讲,这里毫秒延时并不是说何时这些代码会执 行,而只是说到时候会把回调加到任务队列。如果添加到队列后,主线程还被其他任务占用,比如正在 处理用户操作,那么回调就不会马上执行。

时间间隔的问题

知道何时绘制下一帧是创造平滑动画的关键。直到几年前,都没有办法确切保证何时能让浏览器把 下一帧绘制出来。随着的流行和 HTML5 游戏的兴起,开发者发现 setInterval()和 setTimeout()的不精确是个大问题。
浏览器自身计时器的精度让这个问题雪上加霜。浏览器的计时器精度不足毫秒。以下是几个浏览器 计时器的精度情况:
 IE8 及更早版本的计时器精度为 15.625 毫秒;  IE9 及更晚版本的计时器精度为 4 毫秒;
 Firefox 和 Safari 的计时器精度为约 10 毫秒;  Chrome 的计时器精度为 4 毫秒。
IE9 之前版本的计时器精度是 15.625 毫秒,意味着 0~15 范围内的任何值最终要么是 0,要么是 15, 不可能是别的数。IE9 把计时器精度改进为 4 毫秒,但这对于动画而言还是不够精确。Chrome 计时器精 度是 4 毫秒,而 Firefox 和 Safari 是 10 毫秒。更麻烦的是,浏览器又开始对切换到后台或不活跃标签页 中的计时器执行限流。因此即使将时间间隔设定为最优,也免不了只能得到近似的结果。

requestAnimationFrame

Mozilla 的 Robert O’Callahan 一直在思考这个问题,并提出了一个独特的方案。他指出,浏览器知道 CSS 过渡和动画应该什么时候开始,并据此计算出正确的时间间隔,到时间就去刷新用户界面。但对于 JavaScript 动画,浏览器不知道动画什么时候开始。他给出的方案是创造一个名为 mozRequestAnimationFrame() 的新方法,用以通知浏览器某些 JavaScript 代码要执行动画了。这样浏览器就可以在运行某些代码后进 行适当的优化。目前所有浏览器都支持这个方法不带前缀的版本,即 requestAnimationFrame()。
requestAnimationFrame()方法接收一个参数,此参数是一个要在重绘屏幕前调用的函数。

函数就是修改 DOM 样式以反映下一次重绘有什么变化的地方。为了实现动画循环,可以把多个 requestAnimationFrame()调用串联起来,就像以前使用 setTimeout()时一样:
因为 requestAnimationFrame()只会调用一次传入的函数,所以每次更新用户界面时需要再手 动调用它一次。同样,也需要控制动画何时停止。结果就会得到非常平滑的动画。

使用 requestAnimationFrame 551

目前为止,requestAnimationFrame()已经解决了浏览器不知道 JavaScript 动画何时开始的问题, 以及最佳间隔是多少的问题,但是,不知道自己的代码何时实际执行的问题呢?这个方案同样也给出了 解决方法。 19

function updateProgress() {
  var div = document.getElementById("status");
  div.style.width = (parseInt(div.style.width, 10) + 5) + "%";
  if (div.style.left != "100%") {
  requestAnimationFrame(updateProgress);
  }
}
requestAnimationFrame(updateProgress);

传给 requestAnimationFrame()的函数实际上可以接收一个参数,此参数是一个 DOMHighRes- TimeStamp 的实例(比如 performance.now()返回的值),表示下次重绘的时间。这一点非常重要: requestAnimationFrame()实际上把重绘任务安排在了未来一个已知的时间点上,而且通过这个参数 告诉了开发者。基于这个参数,就可以更好地决定如何调优动画了。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值