• 售前

  • 售后

热门帖子
入门百科

详解盒子端CSS动画性能提拔

[复制链接]
麻辣鸡翅 显示全部楼层 发表于 2021-8-16 12:03:48 |阅读模式 打印 上一主题 下一主题
目录


  • 流畅动画的标准
  • 盒子端动画优化
  • 动画性能上报分析
  • 研究结论
  • 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 的实行次数,用于盘算每次动画的帧率 :
  1. var rAF = function () {
  2.     return (
  3.         window.requestAnimationFrame ||
  4.         window.webkitRequestAnimationFrame ||
  5.         function (callback) {
  6.             window.setTimeout(callback, 1000 / 60);
  7.         }
  8.     );
  9. }();
  10. var frame = 0;
  11. var allFrameCount = 0;
  12. var lastTime = Date.now();
  13. var lastFameTime = Date.now();
  14. var loop = function () {
  15.     var now = Date.now();
  16.     var fs = (now - lastFameTime);
  17.     var fps = Math.round(1000 / fs);
  18.     lastFameTime = now;
  19.     // 不置 0,在动画的开头及结尾记录此值的差值算出 FPS
  20.     allFrameCount++;
  21.     frame++;
  22.     if (now > 1000 + lastTime) {
  23.         var fps = Math.round((frame * 1000) / (now - lastTime));
  24.         // console.log('fps', fps); 每秒 FPS
  25.         frame = 0;
  26.         lastTime = now;
  27.     };
  28.     rAF(loop);
  29. }
复制代码
研究结论

以是,我们的目标就是在利用 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> 元素的宽度一样平常会影响其子元素的宽度以及树中各处的节点,因此对于欣赏器来说,结构过程是经常发生的。
4.绘制。绘制是填充像素的过程。它涉及绘出文本、颜色、图像、边框和阴影,根本上包罗元素的每个可视部分。绘制一样平常是在多个外貌(通常称为层)上完成的。
5.合成。由于页面的各部分可能被绘制到多层,由此它们必要按精确顺序绘制到屏幕上,以便精确渲染页面。对于与另一元素重叠的元向来说,这点特别告急,由于一个错误可能使一个元素错误地出现在另一个元素的上层。
当然,不肯定每帧都总是会经过管道每个部分的处理。我们的目标就是,每一帧的动画,对于上述的管道流程,能制止则制止,不能制止则最大限度优化。

优化动画步调

先给出一个步调,调优一个动画,有肯定的引导原则可以依照,一步一步深入动画:

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)
以是,正常而言,在生产情况下,我们可能必要利用如下代码,开启硬件加快:
  1. {
  2.     will-change: transform;
  3.     transform: translateZ(0);
  4. }
复制代码
3.控制频仍动画的层级关系

动画层级的控制的意思是只管让必要进行 CSS 动画的元素的z-index保持在页面最上方,制止欣赏器创建不必要的图形层(GraphicsLayer),可以或许很好的提升渲染性能。
OK,这里又提到了图形层(GraphicsLayer),这是一个欣赏器渲染原理相干的知识(WebKit/blink内核下)。它能对动画进行加快,但同时也存在相应的加快坑!

简朴来说,欣赏器为了提升动画的性能,为了在动画的每一帧的过程中不必每次都重新绘制整个页面。在特定方式下可以触发生成一个合成层,合成层拥有单独的 GraphicsLayer。
必要进举措画的元素包含在这个合成层之下,这样动画的每一帧只必要去重新绘制这个 Graphics Layer 即可,从而到达提升动画性能的目标。
那么一个元素什么时间会触发创建一个 Graphics Layer 层?从现在来说,满足以下恣意情况便会创建层:
       
  • 硬件加快的 iframe 元素(比如 iframe 嵌入的页面中有合成层)   
  • 硬件加快的插件,比如 flash 等等   
  • 利用加快视频解码的<video>元素   
  • 3D 或者 硬件加快的 2D Canvas 元素   
  • 3D 或透视变动 (perspective、transform) 的 CSS 属性   
  • 对本身的 opacity 做 CSS 动画或利用一个动画变动的元素   
  • 拥有加快 CSS 过滤器的元素   
  • 元素有一个包含复合层的后代节点(换句话说,就是一个元素拥有一个子元素,该子元素在本身的层里)   
  • 元素有一个 z-index 较低且包含一个复合层的兄弟元素
本小点中说到的动画层级的控制,原因就在于上面生成层的末了一条:
元素有一个 z-index 较低且包含一个复合层的兄弟元素。
这里是存在坑的地方,首先我们要明确两点:
1.我们渴望我们的动画得到 GPU 硬件加快,以是我们会利用类似transform: translateZ()这样的方式生成一个 Graphics Layer 层。
2.Graphics Layer 虽好,但不是越多越好,每一帧的渲染内核都会去遍历盘算当前全部的 Graphics Layer ,并盘算他们下一帧的重绘区域,以是过量的 Graphics Layer 盘算也会给渲染造成性能影响。
记住这两点之后,回到上面我们说的坑。
假设我们有一个轮播图,有一个 ul 列表,结构如下:
  1. <div class="container">
  2. <div class="swiper">轮播图</div>
  3. <ul class="list">
  4. <li>列表li</li>
  5. <li>列表li</li>
  6. <li>列表li</li>
  7. <li>列表li</li>
  8. </ul>
  9. </div>
复制代码
假设给他们界说如下 CSS:
  1. .swiper {
  2.     position: static;
  3.     animation: 10s move infinite;
  4. }
  5. .list {
  6.     position: relative;
  7. }
  8. @keyframes move {
  9.     100% {
  10.         transform: translate3d(10px, 0, 0);
  11.     }
  12. }
复制代码
由于给.swiper添加了translate3d(10px, 0, 0)动画,以是它会生成一个 Graphics Layer,如下图所示,用开发者工具可以打开层的展示,图形外的黄色边框即代表生成了一个独立的复合层,拥有独立的 Graphics Layer 。

但是!在上面的图中,我们并没有给下面的list也添加任何能触发生成 Graphics Layer 的属性,但是它也同样也有黄色的边框,生成了一个独立的复合层。
原因在于上面那条元素有一个 z-index 较低且包含一个复合层的兄弟元素。我们并不渴望list元素也生成 Graphics Layer ,但是由于 CSS 层级界说原因,下面的 list 的层级高于上面的 swiper,以是它被动的也生成了一个 Graphics Layer 。
利用 Chrome,我们也可以观察到这种层级关系,可以看到.list的层级高于.swiper:

以是,下面我们修改一下 CSS ,改成:
  1. .swiper {
  2.     position: relative;
  3.     z-index: 100;
  4. }
  5. .list {
  6.     position: relative;
  7. }
复制代码
这里,我们明确使得.swiper的层级高于.list,再打开开发者工具观察一下:

可以看到,这一次,.list元素已经没有了黄色外边框,说明此时没有生成 Graphics Layer 。再看看层级图:

此时,层级关系才是我们渴望看到的,.list元素没有触发生成 Graphics Layer 。而我们渴望必要硬件加快的.swiper保持在最上方,每次动画过程中只会独立重绘这部分的区域。
总结
这个坑最早见于张云龙发布的这篇文章CSS3硬件加快也有坑,这里还要总结增补的是:
GPU 硬件加快也会有坑,当我们渴望利用利用类似transform: translate3d()这样的方式开启 GPU 硬件加快,肯定要注意元素层级的关系,只管保持让必要进行 CSS 动画的元素的z-index保持在页面最上方。
Graphics Layer 不是越多越好,每一帧的渲染内核都会去遍历盘算当前全部的 Graphics Layer ,并盘算他们下一帧的重绘区域,以是过量的 Graphics Layer 盘算也会给渲染造成性能影响。
可以利用 Chrome ,用上面介绍的两个工具对本身的页面生成的 Graphics Layer 和元素层级进行观察然后进行相应修改。
上面观察页面层级的 chrome 工具非常吃内存?好像照旧一个处于实验室的功能,分析轻微大一点的页面目面目易直接卡死,以是要多学会利用第一种观察黄色边框的方式检察页面生成的 Graphics Layer 这种方式。

4. 利用 will-change 可以在元素属性真正发生变化之条件前做好对应预备
  1. // 示例
  2. .example {
  3.     will-change: transform;
  4. }
复制代码
上面已经提到过 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动画性能提升的资料请关注草根技术分享别的相干文章!

本帖子中包含更多资源

您需要 登录 才可以下载或查看,没有帐号?立即注册

x

帖子地址: 

回复

使用道具 举报

分享
推广
火星云矿 | 预约S19Pro,享500抵1000!
您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

草根技术分享(草根吧)是全球知名中文IT技术交流平台,创建于2021年,包含原创博客、精品问答、职业培训、技术社区、资源下载等产品服务,提供原创、优质、完整内容的专业IT技术开发社区。
  • 官方手机版

  • 微信公众号

  • 商务合作