柔滑流畅的JavaScript动画的秘密!
用JavaScript创建动画是你可以做的最简单的事情之一。如果你曾经尝试过这样做,那么你很可能已经使用过setTimeout
或者setInterval
功能。
这是一个典型的例子:
|
这段代码会draw
每100ms永远调用一次函数,直到稍后clearInterval
调用该函数。此代码的替代方法是使用setTimeout
函数,而不是此draw
函数内部:
|
对draw
函数的单个调用会启动动画循环,从此它将每100ms重复一次。
帧速率和setInterval
动画的平滑度取决于动画的帧速率。帧速率以每秒帧数(fps)来衡量。电影通常运行速度为24fps,视频速度为30fps。这个数字越高,动画看起来就越平滑。更多的帧,意味着更多的处理,这往往会导致口吃和跳跃。这就是丢帧的意思。由于大多数屏幕的刷新频率为60Hz,所以您应该瞄准的最快帧频为60fps。有些数学的时间!
|
setTimeout和setInterval有什么问题?
好的,你以前做过这一百万次了。这张照片有什么问题?好吧,有几件事。首先,setTimeout
不考虑浏览器中发生了什么。该页面可能隐藏在标签后面,在不需要时占用CPU,或者动画本身可能已从页面滚动到不再需要更新呼叫。Chrome会在隐藏的标签中进行缩小setInterval
和setTimeout
1fps,但这不是所有浏览器都需要依赖的。
其次,setTimeout
只在需要时更新屏幕,而不是在计算机能够时更新。这意味着您的浏览器不得不在重画整个屏幕时重新绘制动画,并且如果您的动画帧速率与重新绘制屏幕不同步,则可能需要更多的处理能力。这意味着更高的CPU使用率和计算机的风扇,或移动设备上的电池耗尽。Nicolas Zakas在解释定时器分辨率对相关文章中动画的影响方面做得非常出色。
另一个考虑是一次动画多个元素。解决这个问题的一种方法是将所有动画逻辑放置在一个区间中,并理解即使特定元素可能不需要用于当前帧的任何动画,也可以运行动画调用。另一种方法是使用不同的时间间隔。这种方法的问题在于,每次在屏幕上移动某个东西时,浏览器都必须重新绘制屏幕。这太浪费了!
requestAnimationFrame来拯救!
为了克服这些效率问题,Mozilla(Firefox制造商)提出了该requestAnimationFrame
功能,后来由WebKit团队(Chrome和Safari)采用并改进了该功能。它提供了一个本机API,用于在浏览器中运行任何类型的动画,可以使用DOM元素,CSS,canvas,WebGL或其他任何类型的动画。
以下是您如何使用它的方法:
|
大!它和setTimeout
版本完全一样,但是requestAnimationFrame
代之以。或者,您可以将参数传递给被调用的函数,例如当前元素正在被动画,如下所示:requestAnimationFrame(draw, element);
您可能已经注意到了,但您并未指定间隔比率。那么这个draw
函数多久调用一次?这一切都取决于您的浏览器和计算机的帧速率,但通常是60fps(这是很酷的,因为您的计算机的显示器通常以60Hz的速率刷新)。这里的关键区别在于,您正在请求浏览器在下一个可用机会绘制动画,而不是以预定间隔绘制。也有人暗示,浏览器可以选择requestAnimationFrame
基于负载,元素可见性(滚动出视图)和电池状态来优化性能。
另外一个好处requestAnimationFrame
就是它将所有的动画组合成一个浏览器重绘。这节省了CPU周期,并让您的设备过上更长久,更幸福的生活。
所以,如果你使用requestAnimationFrame
所有的动画应该变得如丝般顺滑,与你的GPU同步,CPU少得多。如果你浏览到一个新标签页,浏览器会将动画限制为抓取,从而防止它在繁忙时占用计算机。好极了!
听起来很棒!任何问题?
嗯,是。因为这是一个新的API,它目前只能通过供应商前缀在浏览器中使用,例如webkitRequestAnimationFrame
在Chrome和Safari中以及mozRequestAnimationFrame
在Firefox中。浏览器支持并不糟糕,甚至微软也会支持msRequestAnimationFrame
IE 10的版本。
为了避开不同的支持,埃里克·默勒(歌剧),保罗·爱尔兰(谷歌)和蒂诺Zijdel(Tweakers.net)已经创建了一个填充工具,使之简单再次使用:
|
把它放到你的代码中,你可以使用requestAnimationFrame作为自然意图。
一个简单的垫片是可用的,它只使用a setTimeout
作为后备。这也可以,只要你requestAnimationFrame
在循环的开始而不是结束时调用。如果你不这样做,Opera的ErikMöller解释道,动画时序可能变得不可预测。因此,坚持上面的更新可能会更好
如果我想设置帧速率怎么办?
剩下的一个明显问题是:requestAnimationFrame
如果您无法指定帧速率,如何控制动画的时间?游戏通常需要特定的帧率来制作动画。
停止!在跑回去之前setInterval
,您可以使用以下技巧:
|
通过包装requestAnimationFrame
在一个setTimeout
你有你的鱼与熊掌兼得。您的代码可以节省效率,并且可以指定帧速率,最高可达60fps。
更复杂的技术是检查自上次绘制调用以来的毫秒数,并根据时间差异更新动画的位置。例如:
|