不同于传统的PC Web或者是移动WEB,在腾讯视频客厅盒子端,接大屏显示器下,许多能流畅运行于PC端、移动端的Web动画,受限于硬件水平,在盒子端的表现的往往不尽如人意。想要在盒子端跑出高性能接近60 FPS的流畅动画,就必须要刨根问底,深
目录
- 流畅动画的标准
- 盒子端动画优化
- 动画性能上报分析
- 研究结论
- Web 每一帧的渲染
- 优化动画步骤
- 1.精简 DOM ,合理布局
- 2.使用 transform 代替 left、top,减少使用耗性能样式
- 3.控制频繁动画的层级关系
- 4. 使用 will-change 可以在元素属性真正发生变化之前提前做好对应准备
- 5. 使用 dev-tool 时间线 timeline 观察,找出导致高耗时、掉帧的关键操作
- 总结一下
流畅动画的标准
理论上说,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 毫秒内完成。
而每一帧,如果有必要,我们能控制的部分,也是像素至屏幕管道中的关键步骤如下:
优化动画步骤
先给出一个步骤,调优一个动画,有一定的指导原则可以遵循,一步一步深入动画:
1.精简 DOM ,合理布局
这个没什么好说的,如果可以,精简 DOM 结构在任何时候都是对页面有帮助的。
2.使用 transform 代替 left、top,减少使用耗性能样式
现代浏览器在完成以下四种属性的动画时,消耗成本较低:
- position(位置):transform: translate(npx, npx)
- scale(比例缩放):transform: scale(n)
- rotation(旋转) :transform: rotate(ndeg)
- opacity(透明度):opacity: 0...1
如果可以,尽量只使用上述四种属性去控制动画。
不同样式在消耗性能方面是不同的,改变一些属性的开销比改变其他属性要多,因此更可能使动画卡顿。
例如,与改变元素的文本颜色相比,改变元素的box-shadow将需要开销大很多的绘图操作。 改变元素的width可能比改变其transform要多一些开销。如box-shadow属性,从渲染角度来讲十分耗性能,原因就是与其他样式相比,它们的绘制代码执行时间过长。
这就是说,如果一个耗性能严重的样式经常需要重绘,那么你就会遇到性能问题。其次你要知道,没有不变的事情,在今天性能很差的样式,可能明天就被优化,并且浏览器之间也存在差异。
开启 GPU 硬件加速
归根结底,上述四种属性的动画消耗较低的原因是会开启了 GPU 硬件加速。动画元素生成了自己的图形层(GraphicsLayer)。
通常而言,开启 GPU 加速的方法我们可以使用
will-change: transform
这会使声明了该样式属性的元素生成一个图形层,告诉浏览器接下来该元素将会进行 transform 变换,让浏览器提前做好准备。
使用will-change并不一定会有性能的提升,因为即使浏览器预料到会有这些更改,依然会为这些属性运行布局和绘制流程,所以提前告诉浏览器,也并不会有太多性能上的提升。这样做的好处是,创建新的图层代价很高,而等到需要时匆忙地创建,不如一开始直接创建好。
对于 Safari 及一些旧版本浏览器,它们不能识别will-change,则需要使用某种 translate 3D 进行 hack,通常会使用
transform: translateZ(0)
所以,正常而言,在生产环境下,我们可能需要使用如下代码,开启硬件加速:
{
will-change: transform;
transform: translateZ(0);
}
3.控制频繁动画的层级关系
动画层级的控制的意思是尽量让需要进行 CSS 动画的元素的z-index保持在页面最上方,避免浏览器创建不必要的图形层(GraphicsLayer),能够很好的提升渲染性能。
OK,这里又提到了图形层(GraphicsLayer),这是一个浏览器渲染原理相关的知识(WebKit/blink内核下)。它能对动画进行加速,但同时也存在相应的加速坑!
<div class="container">
<div class="swiper">轮播图</div>
<ul class="list">
<li>列表li</li>
<li>列表li</li>
<li>列表li</li>
<li>列表li</li>
</ul>
</div>
假设给他们定义如下 CSS:
.swiper {
position: static;
animation: 10s move infinite;
}
.list {
position: relative;
}
@keyframes move {
100% {
transform: translate3d(10px, 0, 0);
}
}
由于给.swiper添加了translate3d(10px, 0, 0)动画,所以它会生成一个 Graphics Layer,如下图所示,用开发者工具可以打开层的展示,图形外的黄色边框即代表生成了一个独立的复合层,拥有独立的 Graphics Layer 。
.swiper {
position: relative;
z-index: 100;
}
.list {
position: relative;
}
这里,我们明确使得.swiper的层级高于.list,再打开开发者工具观察一下:
4. 使用 will-change 可以在元素属性真正发生变化之前提前做好对应准备
// 示例
.example {
will-change: transform;
}
上面已经提到过 will-change 了。
will-change 为 web 开发者提供了一种告知浏览器该元素会有哪些变化的方法,这样浏览器可以在元素属性真正发生变化之前提前做好对应的优化准备工作。 这种优化可以将一部分复杂的计算工作提前准备好,使页面的反应更为快速灵敏。
值得注意的是,用好这个属性并不是很容易:
在一些低端盒子上,will-change会导致很多小问题,譬如会使图片模糊,有的时候很容易适得其反,所以使用的时候还需要多加测试。
不要将 will-change 应用到太多元素上:浏览器已经尽力尝试去优化一切可以优化的东西了。有一些更强力的优化,如果与 will-change 结合在一起的话,有可能会消耗很多机器资源,如果过度使用的话,可能导致页面响应缓慢或者消耗非常多的资源。
有节制地使用:通常,当元素恢复到初始状态时,浏览器会丢弃掉之前做的优化工作。但是如果直接在样式表中显式声明了 will-change 属性,则表示目标元素可能会经常变化,浏览器会将优化工作保存得比之前更久。所以最佳实践是当元素变化之前和之后通过脚本来切换 will-change 的值。
不要过早应用 will-change 优化:如果你的页面在性能方面没什么问题,则不要添加 will-change 属性来榨取一丁点的速度。 will-change 的设计初衷是作为最后的优化手段,用来尝试解决现有的性能问题。它不应该被用来预防性能问题。过度使用 will-change 会导致生成大量图层,进而导致大量的内存占用,并会导致更复杂的渲染过程,因为浏览器会试图准备可能存在的变化过程,这会导致更严重的性能问题。
给它足够的工作时间:这个属性是用来让页面开发者告知浏览器哪些属性可能会变化的。然后浏览器可以选择在变化发生前提前去做一些优化工作。所以给浏览器一点时间去真正做这些优化工作是非常重要的。使用时需要尝试去找到一些方法提前一定时间获知元素可能发生的变化,然后为它加上 will-change 属性。
5. 使用 dev-tool 时间线 timeline 观察,找出导致高耗时、掉帧的关键操作
1)对比屏幕快照,观察每一帧包含的内容及具体的操作
2)找到掉帧的那一帧,分析该帧内不同步骤的耗时占比,进行有针对性的优化
3)观察是否存在内存泄漏
对于 timeline 的使用用法,这里有个非常好的教程,通俗易懂,可以看看:
浏览器渲染优化 Udacity 课程
总结一下
对于盒子端 CSS 动画的性能,很多方面仍处于探索中,本文大量内容在之前文章已经出现过,这里更多的是归纳总结提炼成可参照执行的流程。
本文的优化方案研究同样适用于 PC Web 及移动 Web
以上就是详解盒子端CSS动画性能提升的详细内容,更多关于盒子端CSS动画性能提升的资料请关注编程学习网其它相关文章!