JS检测浏览器运行环境是否卡顿

3,365 阅读3分钟

一. 什么是FPS

FPS是图像领域中的定义,是指画面每秒传输帧数,通俗来讲就是指动画或视频的画面数。FPS是测量用于保存、显示动态视频的信息数量。每秒钟帧数越多,所显示的动作就会越流畅。通常,要避免动作不流畅的最低是30。某些计算机视频格式,每秒只能提供15帧。

二. 不同帧率的体验

理论上说,FPS 越高,动画会越流畅,目前大多数设备的屏幕刷新率为 60 次/秒,所以通常来讲 FPS 为 60 frame/s 时动画效果最好,也就是每帧的消耗时间为 16.67ms。
  • 帧率能够达到 50 ~ 60 FPS 的动画将会相当流畅,让人倍感舒适;
  • 帧率在 30 ~ 50 FPS 之间的动画,因各人敏感程度不同,舒适度因人而异;
  • 帧率在 30 FPS 以下的动画,让人感觉到明显的卡顿和不适感;
  • 帧率波动很大的动画,亦会使人感觉到卡顿

通过计算fps值,可以大体知道当前机器是不是存在卡顿的情况

三. JS如何计算机器FPS

  • Frame Timing API

借助 Web Performance Timing API 中的 Frame Timing API,可以轻松的拿到每一帧中,主线程以及合成线程的时间,或者直接拿到每一帧的耗时。

const rendererEvents = window.performance.getEntriesByType('renderer')
const compositeEvents = window.performance.getEntriesByType('composite')

获取 Render 主线程和合成线程的记录,每条记录包含的信息基本如下:

{   
    sourceFrameNumber: 120,
    startTime: 1342.549374253,   
    duration: 10.654313323
}

每个记录都包括唯一的 Frame Number、Frame 开始时间以及持续时间。根据 duration 就可以知道该帧是否达到 16.66ms 的标准,同时根据单位时间记录数(Frame)的个数就能算出主线程或者合成线程每秒的帧率。Frame Timing API 虽好,但是现在 Frame Timing API 的兼容性不算很友好,暂时还没有任何浏览器支持,处于实验性阶段

  • requestAnimationFrame API

requestAnimationFrame 大家应该都不陌生,方法告诉浏览器您希望执行动画并请求浏览器调用指定的函数在下一次重绘之前更新动画,当你准备好更新屏幕画面时你就应用此方法。这会要求你的动画函数在浏览器下次重绘前执行。回调的次数常是每秒 60 次,大多数浏览器通常匹配 W3C 所建议的刷新率。

JS语法:

/*
 *
 * 定义raf
 * @param { Function } callback
*/
raf (callback) { 
    const fn = window.requestAnimationFrame ||
        window.mozRequestAnimationFrame ||
        window.webkitRequestAnimationFrame
    return fn(callback)
}

正常而言 requestAnimationFrame 这个方法在一秒内会执行 60 次,也就是不掉帧的情况下。假设动画在时间 A 开始执行,在时间 B 结束,耗时 x ms。而中间 requestAnimationFrame 一共执行了 n 次,则此段动画的帧率大致为:n / (B - A),核心代码如下:

/*
 *
 * 循环执行 
 */  
loop () {    
    this.loopBeginTime = new Date().valueOf()
    this.loopId = window.setTimeout(() => {
        this.loopEndTime = new Date().valueOf()
        const fps = this.calculate()
        this.count = 0
        this.loop()
    }, this.interval)  }
/*
 * 
 * raf一秒内执行次数
*/ 
fpsTick () {    
    this.fpsTickId = this.raf(() => { 
        this.count++ 
        this.fpsTick()
    })
} 
/*
 * 
 * 计算时间范围内执行次数
 */  
calculate () {
    const during = this.loopEndTime - this.loopBeginTime
    const runCount = during / this.count
    return +(1000 / runCount).toFixed(this.decimals)
}

四. 数据上报

  每个公司都有数据上报工具,这个自行接入就行