流畅动画的标准
理论上说,FPS 越高,动画会越流畅,目前大多数设备的屏幕刷新率为 60 次/秒,所以通常来讲 FPS 为 60frame/s 时动画效果最好,也就是每帧的消耗时间为 16.67ms。
直观感受,不同帧率的体验
帧率能够达到 50 ~ 60 FPS 的动画将会相当流畅,让人倍感舒适;
帧率在 30 ~ 50 FPS 之间的动画,因各人敏感程度不同,舒适度因人而异;
帧率在 30 FPS 以下的动画,让人感觉到明显的卡顿和不适感;
帧率波动很大的动画,亦会使人感觉到卡顿。
盒子端动画优化在腾讯视频客厅盒子端,Web 动画未进行优化之前,一些复杂动画的帧率仅有 10 ~ 30 FPS,卡顿感非常明显,带来很不好的用户体验。
而进行优化之后,能将 10 ~ 30 FPS的动画优化至 30 ~ 60 FPS,虽然不算优化到最完美,但是当前盒子硬件的条件下,已经算是非常大的进步。
盒子端 Web 动画性能比较
首先先给出在盒子端不同类型的Web 动画的性能比较。经过对比,在盒子端 CSS 动画的性能要优于 Javascript 动画,而在 CSS 动画里,使用 GPU 硬件加速的动画性能要优于不使用硬件加速的性能。
所以在盒子端,实现一个 Web 动画,优先级是:
GPU 硬件加速 CSS 动画 > 非硬件加速 CSS 动画 > Javascript 动画
动画性能上报分析要有优化,就必须得有数据做为支撑。对比优化前后是否有提升。而对于动画而言,衡量一个动画的标准也就是 FPS 值。
所以现在的关键是如何计算出每个动画运行时的帧率,这里我使用的是requestAnimationFrame这个函数近似的得到动画运行时的帧率。
考虑到盒子都是安卓系统,且大多版本较低且硬件性能堪忧,导致一是许多高级 API 无法使用,二是这里只是近似得到动画帧率
原理是,正常而言requestAnimationFrame这个方法在一秒内会执行 60 次,也就是不掉帧的情况下。假设动画在时间 A 开始执行,在时间 B 结束,耗时 x ms。而中间requestAnimationFrame一共执行了 n 次,则此段动画的帧率大致为:n / (B - A)。
核心代码如下,能近似计算每秒页面帧率,以及我们额外记录一个allFrameCount,用于记录 rAF 的执行次数,用于计算每次动画的帧率 :
var rAF = function () { return ( window.requestAnimationFrame || window.webkitRequestAnimationFrame || function (callback) { window.setTimeout(callback, 1000 / 60); } ); }(); var frame = 0; var allFrameCount = 0; var lastTime = Date.now(); var lastFameTime = Date.now(); var loop = function () { var now = Date.now(); var fs = (now - lastFameTime); var fps = Math.round(1000 / fs); lastFameTime = now; // 不置 0,在动画的开头及结尾记录此值的差值算出 FPS allFrameCount++; frame++; if (now > 1000 + lastTime) { var fps = Math.round((frame * 1000) / (now - lastTime)); // console.log('fps', fps); 每秒 FPS frame = 0; lastTime = now; }; rAF(loop); }
研究结论所以,我们的目标就是在使用 GPU 硬件加速的基础之上,更深入的去优化 CSS 动画,先给出最后的一个优化步骤方案:
1.精简 DOM ,合理布局
2.使用 transform 代替 left、top,减少使用耗性能样式
3.控制频繁动画的层级关系
4.考虑使用 will-change
5.使用 dev-tool 时间线 timeline 观察,找出导致高耗时、掉帧的关键操作
下文会有每一步骤的具体分析解释。
Web 每一帧的渲染要想达到 60 FPS,每帧的预算时间仅比 16 毫秒多一点 (1 秒/ 60 = 16.67 毫秒)。但实际上,浏览器有整理工作要做,因此您的所有工作需要尽量在 10 毫秒内完成。
而每一帧,如果有必要,我们能控制的部分,也是像素至屏幕管道中的关键步骤如下:
完整的像素管道JS / CSS > 样式 > 布局 > 绘制 > 合成:
1.JavaScript。一般来说,我们会使用 JavaScript 来实现一些视觉变化的效果。比如用 jQuery 的 animate 函数做一个动画、对一个数据集进行排序或者往页面里添加一些 DOM 元素等。当然,除了 JavaScript,还有其他一些常用方法也可以实现视觉变化效果,比如:CSS Animations、Transitions 和 Web Animation API。
2.样式计算。此过程是根据匹配选择器(例如 .headline 或 .nav > .nav__item)计算出哪些元素应用哪些 CSS 3. 规则的过程。从中知道规则之后,将应用规则并计算每个元素的最终样式。
3.布局。在知道对一个元素应用哪些规则之后,浏览器即可开始计算它要占据的空间大小及其在屏幕的位置。网页的布局模式意味着一个元素可能影响其他元素,例如 <body> 元素的宽度一般会影响其子元素的宽度以及树中各处的节点,因此对于浏览器来说,布局过程是经常发生的。