流畅动画的标准
首先,理清一些概念。FPS 表示的是每秒钟画面更新次数。我们平时所看到的连续画面都是由一幅幅静止画面组成的,每幅画面称为一帧,FPS 是描述“帧”变化速度的物理量。
理论上说,FPS 越高,动画会越流畅,目前大多数设备的屏幕刷新率为 60 次/秒,所以通常来讲 FPS 为 60 frame/s 时动画效果最好,也就是每帧的消耗时间为 16.67ms。
当然,经常玩 FPS 游戏的朋友肯定知道,吃鸡/CSGO 等 FPS 游戏推荐使用 144HZ 刷新率的显示器,144Hz 显示器特指每秒的刷新率达到 144Hz 的显示器。相较于普通显示器每秒60的刷新速度,画面显示更加流畅。因此144Hz显示器比较适用于视角时常保持高速运动的第一人称射击游戏。
不过,这个只是显示器提供的高刷新率特性,对于我们 Web 动画而言,是否支持还要看浏览器,而大多数浏览器刷新率为 60 次/秒。
直观感受,不同帧率的体验:
帧率能够达到 50 ~ 60 FPS 的动画将会相当流畅,让人倍感舒适;
帧率在 30 ~ 50 FPS 之间的动画,因各人敏感程度不同,舒适度因人而异;
帧率在 30 FPS 以下的动画,让人感觉到明显的卡顿和不适感;
帧率波动很大的动画,亦会使人感觉到卡顿。
OK,那么我们该如何准确的获取我们页面动画当前的 FPS 值呢?
法一:借助 Chrome 开发者工具Chrome 提供给开发者的功能十分强大,在开发者工具中,我们进行如下选择调出FPS meter选项:
通过这个按钮,可以开启页面实时 Frame Rate (帧率) 观测及页面 GPU 使用率。
缺点:
这个只能一次观测一到几个页面,而且需要人工实时观测
数据只能是主观感受,并没有一个十分精确的数据不断上报或者被收集
因此,我们需要更加智能的方法。
法二:借助 Frame Timing API在介绍下面这种方法前,继续做一些基础知识的科普。
Blink 内核早期架构以 Chrome 浏览器内核 Blink 渲染页面为例。对早期的 Chrome 浏览器而言,每个页面 Tab 对应一个独立的 renderer 进程,Renderer 进程中包含了主线程和合成线程。早期 Chrome 内核架构:
其中,主线程主要负责:
Javascript 的计算与执行
CSS 样式计算
Layout 计算
将页面元素绘制成位图(paint),也就是光栅化(Raster)
将位图给合成线程
合成线程则主要负责:
将位图(GraphicsLayer 层)以纹理(texture)的形式上传给 GPU
计算页面的可见部分和即将可见部分(滚动)
CSS 动画处理
通知 GPU 绘制位图到屏幕上
OK,云里雾里的,什么东西。其实知道了这两个线程之后,下一个概念是厘清 CSS 动画与 JS 动画的细微区别(当然它们都是 Web 动画)。
JS 动画与 CSS 动画的细微区别对于 JS 动画而言,它们运行时的帧率即是主线程和合成线程加起来消耗的时间。对于流畅动画而言,我们希望它们每一帧的耗时保持在 16.67ms 之内;
而对于 CSS 动画而言,由于其流程不受主线程的影响,所以希望能得到合成线程的消耗的时间,而合成线程的绘制频率也反映了滚动和 CSS 动画的流程性。
上面主要想得出的一个结论是。如果我们能够知道主线程和合成线程每一帧消耗的时间,那么我们就能大致得出对应的 Web 动画的帧率。那么上面说到的Frame Timing API是否可以帮助我们拿到这个时间点呢。
什么是 Frame Timing API ?Frame Timing API 是 Web Performance Timing API 标准中的其中一位成员。
Web Performance Timing API是 W3C 推出的一套性能API 标准,用于帮助开发者对网站各方面的性能进行精确的分析与控制,提升 Web 网站性能。
它包含许多子类 API,完成不同的功能,大致如下(摘自使用性能API快速分析web前端性能,当然你也可以看英文原版介绍:Web Performance Timing API):
怎么使用呢?以Navigation Timing, Performance Timeline, Resource Timing为例子,对于兼容它的浏览器,它以只读属性的形式对外暴露挂载在window.performance上。
在调试台 console 中打印window.performance,查看其中的 timing 属性:
这对象内一连串的变量表示什么呢,它表示我们页面整个加载过程中每一个重要的时间点,可以详细看看这张图: