这段时间在研究微信的小游戏开发,看到requestAnimationFrame这个函数不明所以,查了不少资料依然一知半解。后来一段演示让我豁然开朗。
先看下AI怎么说:
- 起源:最初由浏览器厂商引入(如 WebKit 的
webkitRequestAnimationFrame
),用于优化动画性能。 - 标准化:2013 年纳入 W3C 规范 Timing control for script-based animations,成为无前缀的标准 API。
- 功能目标:替代
setTimeout
/setInterval
实现动画,自动匹配屏幕刷新率(通常 60Hz),避免过度渲染。
据说这个 requestAnimationFrame 函数就是用来替代 setTimeout
和setInterval这两个函数的。这两个函数的作用想必很多人都了解,就是设置一个时间间隔,让指定的函数在每个时间间隔执行。比如下面的代码:
function printTime() {
console.log(new Date().toLocaleTimeString());
}
const intervalId = setInterval(printTime, 1000);
通过setInterval让printTime()函数每间隔一秒就执行一次。或者也可以用setTimeout实现:
function repeat() {
// 你的任务逻辑
console.log("执行任务,时间:", new Date().toLocaleTimeString());
// 递归调用 setTimeout,实现间隔
setTimeout(repeat, 1000);
}
// 启动定时任务
repeat();
据说,以前的前端或者网页开发人员,为了实现动画效果,仿效电影的每秒在屏幕上输出24帧的原理,让显示器在一秒内输出若干帧,就达到了动画的效果。那一秒要输出多少帧呢?一般而言是60帧,为什么是60而不是其他数值?因为绝大多数屏幕的刷新率为 60Hz(每秒刷新 60 次),每帧间隔约 16.67ms。若动画帧率匹配刷新率,可实现最流畅的视觉效果。
后来,网页上的动画开发的需求可能是越来越多,于是requestAnimationFrame应运而生。这个函数不是每个环境都支持。我试过了,在原生的node.js环境下不能支持,想也容易明白,node.js对动画好像没什么需求。所以我们直接在浏览器环境中运行:打开edge或者其他浏览器->按下f12->在控制台复制如下代码:
let animationID;
function startAnimation() {
animationID = requestAnimationFrame(animate);
}
function animate() {
console.log("动画执行中"+(new Date()).toLocaleString());
animationID = requestAnimationFrame(animate); // 循环调用
}
function stopAnimation() {
cancelAnimationFrame(animationID); // 通过ID停止
}
startAnimation();
当敲回车让代码执行后,我们看到以下的输出:
注意看最前面的数字,有的是52,更多的是60,最后那个29是还在执行累计中,最后也应该差不多是累计到60。
而笔者的显示器是60hz,也就是说每秒钟屏幕会整个刷新60次。而requestAnimationFrame与上面两个函数的最大差别是,它不需要手动指定时间间隔,自动匹配显示器的刷新率。假如用setInterval实现类似的输出,则代码大概如下:
var i = 0;
// 示例:每隔1000/60秒打印当前时间
function printTime() {
console.log(`${++i} :`+new Date().toLocaleTimeString());
}
// 启动定时器(间隔1000毫秒)
const intervalId = setInterval(printTime, 1000/60);
// 5秒后停止定时器
setTimeout(() => {
clearInterval(intervalId);
console.log("定时器已停止");
}, 5000);
1000/60是个硬编码,假如在一台刷新率120hz的显示器上,这个地方估计得改。但requestAnimationFrame会自动匹配。下面我更改下我显示器的刷新率,改成50hz。再次运行浏览器的代码,结果如下:
看到最前面的重要次数变成了50次,也就是说requestAnimationFrame根据当前显示器每秒刷新50次的频率,同步更新输出。