window.requestAnimationFrame()
告诉浏览器——你希望执行一个动画,并且要求浏览器在下次重绘之前调用指定的回调函数更新动画。该方法需要传入一个回调函数作为参数,该回调函数会在浏览器下一次重绘之前执行。
window.requestAnimationFrame(callback);
callback:下一次重绘之前更新动画帧所调用的函数 (即上面所说的回调函数)。该回调函数会被传入DOMHighResTimeStamp参数,该参数与performance.now()的返回值相同,它表示requestAnimationFrame() 开始去执行回调函数的时刻。
一个 long 整数,请求 ID ,是回调列表中唯一的标识。是个非零值,没别的意义。你可以传这个值给 window.cancelAnimationFrame() 以取消回调函数。
准备更新动画时调用此方法,这将使浏览器在下一次重绘之前调用你传入给该方法的动画函数 (即你的回调函数);
回调函数执行次数通常是每秒 60 次,但在大多数遵循 W3C 建议的浏览器中,回调函数执行次数通常与 浏览器屏幕刷新次数 相匹配;
为了提高性能和电池寿命,因此在大多数浏览器里,当requestAnimationFrame() 运行在 后台标签页 或者隐藏的 iframe标签 里时,requestAnimationFrame() 会被暂停调用以提升性能和电池寿命;
回调函数会被传入DOMHighResTimeStamp参数,DOMHighResTimeStamp指示当前被 requestAnimationFrame() 排序的回调函数被触发的时间,在帧率控制中用这里传入的时间来计算时间差个人认为比较准备。在同一个帧中的多个回调函数,它们每一个都会接受到一个相同的时间戳,即使在计算上一个回调函数的工作负载期间已经消耗了一些时间。该时间戳是一个十进制数,单位毫秒,最小精度为 1ms(1000μs)。
定义两个描述:“标准帧”:不干涉默认requestAnimationFrame回调次数时的帧集合;“预计帧”:期望控制的帧集合;
方法一:默认帧数中取适当次数更新
不修改标准帧,只判断最近一次标准帧和上一次预期帧记录时间对比,看是否大于等于预计帧的间隔,大于则让希望被控制渲染的内容更新。如图中使用方法一预计在节点①更新的内容,实际是在标准帧中到第②节点才会去更新的。
方法二:用延时控制帧率更新的频率
修改标准帧,用setTimeout延时调用requestAnimationFrame,在方法二中让标准帧本该在①渲染的直接延时到方法二的①节点更新,达到帧数控制的效果。
以下代码是基于《Three.js学习七——播放模型动画时模型沿着轨迹移动》中代码修改的,引入了stats性能插件监控一下帧率和一些辅助变量,修改了部分animate方法内容。
import Stats from "./three.js-master/examples/jsm/libs/stats.module.js";
let stats = null;
function initStats() {
stats = new Stats(); // 引入stats性能插件监控一下帧率
stats.domElement.style.position = "absolute";
stats.domElement.style.left = "0px";
stats.domElement.style.up = "0px";
document.body.appendChild(stats.domElement);
};
let timeS = 0;
let t = 0;
const FPS = 60;
const renderT = (1 / FPS) * 1000; //单位秒 间隔多长时间渲染渲染一次
/* 方法一animate */
function animate(time) {
if (time !== undefined) {
t = time - timeS; // 计算时间间隔
}
if (t >= renderT) {
stats.update(); // 更新stats
// 更新动画帧
if (mixer) {
mixer.update(clock.getDelta());
}
moveOnCurve();
timeS = time; // 更新时间
}
renderer.render(scene, camera);
requestAnimationFrame(animate);
};
效果:
/* 方法二animate */
function animate(time) {
if (time !== undefined) {
t = renderT - (time - timeS) % renderT;
}
stats.update();
// 更新动画帧
if (mixer) {
mixer.update(clock.getDelta());
}
moveOnCurve();
timeS = time; // 更新时间
renderer.render(scene, camera);
// 延时回调
setTimeout(() => {
requestAnimationFrame(animate);
}, t)
};
效果:
方法 | 要点 | 优缺点 |
---|---|---|
方法一 | 判断帧间的时间间隔,默认帧数中取适当次数更新 | 正常运行时帧数非常稳定,一般键鼠操作不会有明显影响 |
方法二 | 计算预计帧间时间延时,用延时控制帧率更新的频率 | 由于控制了整个标准帧刷新正常运行时帧数可能因为键鼠操作导致帧率上下浮动较方法一明显 |