我的想法
但是我录制的fps有时会超过70,为什么?
下面是我的伪代码
const INITIAL_FPS = 0;
export const useReportFPS = () => {
const lastRecordTime = useRef(Date.now());
const fps = useRef(INITIAL_FPS);
const loopShouldRun = useRef(false);
const timerId = useRef<NodeJS.Timer | null>(null);
const clearTimer = useCallback(() => {
if (timerId.current) {
clearTimeout(timerId.current);
timerId.current = null;
}
}, []);
const fragmentation = useRef((callback: () => void) => {
// 同一时刻内存中只有一个 fps 定时器
clearTimer();
timerId.current = setTimeout(() => {
callback();
timerId.current = null;
}, 1000 / 60);
});
const handleFps = useCallback((cost: number, reportFPS: number) => {
/** cost 时间段内经历了几个整数秒 */
const count = Math.floor(cost / 1000);
// 正常情况下,两次 setTimeout 调用间隔小于 2 s 上报一次 fps
console.log('fps', reportFPS);
// 异常情况下,两次 setTimeout 调用间隔大于 N + 1 s,后 N s 的 fps 认为是 0
for (let i = 1; i < count; i++) {
console.log('fps', reportFPS);
}
}, []);
/** 递归调用计算和上报 fps */
const recursiveCollectFPS = useCallback(() => {
fragmentation.current(() => {
// 页面不可见或标识位为 false 时停止递归调用
if (loopShouldRun.current === false || document.hidden) {
return;
}
fps.current += 1;
const current = Date.now();
const cost = current - lastRecordTime.current;
if (cost > 1000) {
handleFps(cost, fps.current);
lastRecordTime.current = current;
fps.current = INITIAL_FPS;
}
recursiveCollectFPS();
});
}, []);
const startCollectFPS = useCallback(() => {
loopShouldRun.current = true;
fps.current = INITIAL_FPS;
lastRecordTime.current = Date.now();
recursiveCollectFPS();
}, []);
const onVisibilitychange = useCallback(() => {
if (document.visibilityState === 'visible') {
// 页面由不可见 -> 可见时,重新开始计算 fps
startCollectFPS();
} else {
clearTimer();
}
}, []);
const stopCollectFPS = useCallback(() => {
loopShouldRun.current = false;
clearTimer();
document.removeEventListener('visibilitychange', onVisibilitychange);
}, []);
const init = useCallback(() => {
document.addEventListener('visibilitychange', onVisibilitychange);
startCollectFPS();
}, []);
useEffect(() => {
init();
return () => {
stopCollectFPS();
};
}, []);
};
预计: fps 是这样计算的 <= 64
有多种原因导致超时可能需要比预期更长的时间
根据 HTML 标准中的规定,一旦对 setTimeout 的嵌套调用被安排 5 次,浏览器将强制执行 4 毫秒的最小超时。
为了减少后台选项卡的负载(以及相关的电池使用量),浏览器将在非活动选项卡中强制执行最小超时延迟。
Firefox 对它识别为跟踪脚本的脚本强制执行额外的限制。前台运行时,节流最小延迟仍为4ms。然而,在后台选项卡中,限制最小延迟为 10,000 毫秒,即 10 秒,在文档首次加载后 30 秒生效。
请参阅跟踪保护了解更多详情。
如果页面(或操作系统/浏览器)正忙于其他任务,超时也可能比预期晚触发。需要注意的一个重要情况是,在调用 setTimeout() 的线程终止之前,函数或代码片段无法执行。
function foo() {
console.log("foo has been called");
}
setTimeout(foo, 0);
console.log("After setTimeout");