过去,JavaScript 中只有一种方法可以执行定时循环:setInterval()
。如果您需要以相当快的速度重复某些操作(但不像 for
循环那样快),则可以使用它。为了动画的目的,目标是每秒六十“帧”以使其看起来流畅,因此您将运行如下循环:
setInterval(function() {
// animiate something
}, 1000/60);
现在有了一个更好的替代方案。 Paul Irish 在 两年前引入了 requestAnimationFrame
。我并没有太多要补充的,我只是之前从未真正使用过它,现在我使用了,所以我想帮助传播这个消息并撰写一篇关于其基本用法的文章。
为什么更好?
正如 Paul 所解释的
- 浏览器可以对其进行优化,因此动画将更加流畅
- 非活动选项卡中的动画将停止,从而使 CPU 能够保持闲置状态
- 更节能
最简单的示例
function repeatOften() {
// Do whatever
requestAnimationFrame(repeatOften);
}
requestAnimationFrame(repeatOften);
调用它一次以启动它,然后您的函数会递归调用自身。
启动和停止
requestAnimationFrame
返回一个 ID,您可以使用它来取消它,就像 setTimeout
或 setInterval
一样。此处使用的 jQuery 仅用于演示简单的动画和绑定事件。
var globalID;
function repeatOften() {
$("<div />").appendTo("body");
globalID = requestAnimationFrame(repeatOften);
}
$("#start").on("click", function() {
globalID = requestAnimationFrame(repeatOften);
});
$("#stop").on("click", function() {
cancelAnimationFrame(globalID);
});
此示例
Check out this Pen!
浏览器支持
请参阅 Can I Use… 表格。
唯一值得注意的问题是 IE 9-、iOS 5- 和 Android。但实际上这不是问题,因为
垫片
像许多高级 Web 功能一样,最好在可用时使用它,并在无法使用时回退到可用的内容。可能最好只是 参考此 Gist。只需在使用 requestAnimationFrame
或 cancelAnimationFrame
之前将其包含在任何位置即可。
使用此方法,您可以在任何浏览器中使用 requestAnimationFrame
。
稍微复杂的示例
我在制作一个简单的演示来更好地学习 canvas 时了解了这一点
查看 Chris Coyier 在 CodePen 上的笔 超简单的 requestAnimationFrame (@chriscoyier)。
实际上更复杂的是同时运行多个使用此方法的动画(仍然可以正常回退)。如果您知道一些示例,请随时在评论中链接这些内容。
您可以使您的“最简单的示例”更简单 :)
是的,为了简单起见,它有效,但我不会建议以这种方式启动动画,因为它将在调用的那一刻渲染,而不是在浏览器实际空闲时渲染,因此它错过了 requestAnimationFrame 的意义。
或者 **更** 简单
在第一个示例中,多次点击“开始”。:P
哦,是的,我们仍然必须为此使用供应商前缀,但它是一种很棒的方法。网络处于持续演变之中……
上面提到的垫片处理了这个问题,以及一些其他小问题,以确保一切正常工作(例如回退到 setTimeout 并处理取消)。
这是第一个示例的一个分支,但我添加了一些代码,以便如果您多次点击按钮,它不会崩溃。
http://cdpn.io/cCoFf
此外,我刚刚意识到此演示可以成为使用 css nth 类型选择器制作图案的一种好方法。
这真的很棒。我想知道是否可以将图像刻录到其中。
当然可以,如果您算出第 n 个正方形的模式。
首先感谢您提供这项非常棒的创新!我有一些问题,为什么 request Animation Frame 不会让您更改帧率?为什么 60fps 是默认值,为什么不能更改?或者可以更改吗?
您可以通过将其包装在 setTimeout 中来实现!本文中有更多信息:http://creativejs.com/resources/requestanimationframe/
我在 paulirish 的博客上阅读了一篇教程,他建议您将函数的自我调用移动到函数的开头,以便在使用 setTimeout 回退时更接近 60fps
http://www.paulirish.com/2011/requestanimationframe-for-smart-animating/
我在尝试使用作为类方法的动画函数时遇到了一些麻烦。它需要访问类中的其他属性和方法,但我获得的上下文是 Window 对象。
我尝试过使用
call
和bind
,但没有成功。有什么想法吗?猜测它不再相关,但以下两者中的任何一个都应该可以工作
var animId = requestAnimationFrame(o.anim.bind(o)); //内置绑定
或
var animId = requestAnimationFrame(function(){o.anim()}); //手动绑定
在 Safari (v5.1.7) 中,启动-停止演示效果很糟糕,在我的测试中,在 FF(v26) 和 Opera (v17.0) 中效果很差。
它仅在 Chrome(30) 和 IE(v10) 上运行良好。
因此,我认为实现它是在浪费时间。只需坚持使用 setInterval 函数,步长为 16.67ms (60hz)。
不幸的是,浏览器开发和标准化背后的那些蠢货仍然停留在 70 年代或更早的技术中,当时引入了 genlock 来使摄像机与 VCR 同步,他们难以置信的愚蠢和粗心仍然影响着我们所有人。
嗨,Chris,关于 requestanimationframe 的精彩讲座,我需要知道,如何使用此函数控制速度?
我希望最好的选择是根据它传递给回调的时间戳参数来确定速度
或者,您可以使用外部 setInterval 来移动元素,并在 requestAnimationFrame 中绘制它们。
我认为您无法更改 requestAnimationFrame 的触发频率,尽管使用不同刷新率的显示器可能有效,但我认为 60Hz 是通用的。
您说“递归调用自身”……但是,请注意,这与人们通常认为的递归(以及它带来的堆栈空间等副作用)有所不同。