CSS3 动画卡顿性能优化解决方案--摘抄

最近在开发小程序,与vue相似,它们都有生命周期这回事。css

  • onLoad 监听页面加载html

  • onReady 监听页面初次渲染完成前端

  • onShow 监听页面显示vue

究竟是什么意思?android

因此这又触碰到了个人知识盲区,不过项目在磕磕绊绊中完成的差很少了,可是遇到了CSS3动画渲染的性能问题,因此我也是被逼的,再回过头来从浏览器渲染网页的流程出发,去找动画卡顿的症结。ios

浏览器渲染网页的流程以下:css3

  • 使用 HTML 建立文档对象模型(DOM)web

  • 使用 CSS 建立 CSS 对象模型(CSSOM)chrome

  • 基于 DOM 和 CSSOM 执行脚本(Scripts)json

  • 合并 DOM 和 CSSOM 造成渲染树(Render Tree)

  • 使用渲染树布局(Layout)全部元素

  • 渲染(Paint)全部元素

能够结合Alon的这篇前端性能优化和安卓开发者选项的显示页面布局。

安卓开发者选项的显示页面布局

如何判断手机app是native,webview仍是hybird?

简单说下,app中的一大块是白色的没有红线标记出来的,可是上面有按钮,图片等时,就是webview,也就是经过一个伪浏览器去请求到的数据,断网时打开app没有任何东西显示在上面

onLoad 监听页面加载

在渲染完界面以后,也就是经过.json中的配置项生成native界面后,开始渲染webview的部分,一个页面只会调用一次。

onReady 监听页面初次渲染完成

一个页面只会调用一次,表明页面已经准备稳当,能够和视图层进行交互。

onShow 监听页面显示

每次打开页面都会去调用其中的函数。

咱们的动画应该放在哪里?

应该放在onShow里,由于这样我每次打开都能看到动画。

为何会卡顿?

有一个前提必需要提,前端开发者们都知道,浏览器是单线程运行的。可是咱们要明确如下几个概念:单线程,主线程和合成线程。

虽说浏览器执行js是单线程执行(注意,是执行,并非说浏览器只有1个线程,而是运行时,runing),但实际上浏览器的2个重要的执行线程,这 2 个线程协同工做来渲染一个网页:主线程和合成线程。

通常状况下,主线程负责:运行 JavaScript;计算 HTML 元素的 CSS 样式;页面的布局;将元素绘制到一个或多个位图中;将这些位图交给合成线程。

相应地,合成线程负责:经过 GPU 将位图绘制到屏幕上;通知主线程更新页面中可见或即将变成可见的部分的位图;计算出页面中哪部分是可见的;计算出当你在滚动页面时哪部分是即将变成可见的;当你滚动页面时将相应位置的元素移动到可视区域。

那么为何会形成动画卡顿呢?

缘由就是主线程和合成线程的调度不合理。

下面来详细说一下调度不合理的缘由。

在使用height,width,margin,padding做为transition的值时,会形成浏览器主线程的工做量较重,例如从margin-left:-20px渲染到margin-left:0,主线程须要计算样式margin-left:-19px,margin-left:-18px,一直到margin-left:0,并且每一次主线程计算样式后,合成进程都须要绘制到GPU而后再渲染到屏幕上,先后总共进行20次主线程渲染,20次合成线程渲染,20+20次,总计40次计算。

主线程的渲染流程,能够参考浏览器渲染网页的流程:

  • 使用 HTML 建立文档对象模型(DOM)

  • 使用 CSS 建立 CSS 对象模型(CSSOM)

  • 基于 DOM 和 CSSOM 执行脚本(Scripts)

  • 合并 DOM 和 CSSOM 造成渲染树(Render Tree)

  • 使用渲染树布局(Layout)全部元素

  • 渲染(Paint)全部元素**

也就是说,主线程每次都须要执行Scripts,Render Tree ,Layout和Paint这四个阶段的计算。

而若是使用transform的话,例如tranform:translate(-20px,0)到transform:translate(0,0),主线程只须要进行一次tranform:translate(-20px,0)到transform:translate(0,0),而后合成线程去一次将-20px转换到0px,这样的话,总计1+20计算。

可能会有人说,这才提高了19次,有什么好性能提高的?

假设一次10ms。

那么就减小了约190ms的耗时。

会有人说,辣鸡,才190ms,无所谓。

那么若是margin-left是从-200px到0呢,一次10ms,10ms*199≈2s。

还会有人说,辣鸡,也就2s,无所谓。

你忘了单线程这回事了吗?

若是网页有3个动画,3*2s=6s,就是6s的性能提高。

因为数据是猜想的,因此暂时不考虑其真实性,文章后面我使用chrome devtools的performance作了一个实验。

要知道,在"客户至上"的今天,好的用户体验是全部产品的必须遵照的一条规则,不管是对于开发者仍是产品经理,追求极致的性能都是咱们打造一个好的产品所必备的品质。

可能看了个人略不专业的分析后,你们对主线程,合成线程以及它们在2种性能不一样动画方案上的工做流程还不是很了解,能够去看一篇翻译过来的博客(英文原版连接已经失效了):深刻浏览器理解CSS animations 和 transitions的性能问题

这篇文章完美讲述了浏览器主线程和合成线程的区别,而且举了一个高度从100px变化到200px的2种动画方案的对比,对主线程和合成线程的整个工做流程作了很详尽的讲解,真心建议认真阅读一遍。

回过头来总结下,css3动画卡顿的解决方案:

在使用css3 transtion作动画效果时,优先选择transform,尽可能不要使用height,width,margin和padding。

transform为咱们提供了丰富的api,例如scale,translate,rotate等等,可是在使用时须要考虑兼容性。但其实对于大多数css3来讲,mobile端支持性较好,desktop端支持性须要格外注意。

补充:为了加强本文的说服力,特意回家作了一个实验,代码以下。

  1. <!DOCTYPE html>

  2. <html>

  3. <head>

  4.  <meta charset="utf-8" />

  5.  <meta http-equiv="X-UA-Compatible" content="IE=edge">

  6.  <title>Page Title</title>

  7.  <meta name="viewport" content="width=device-width, initial-scale=1">

  8.  <style>

  9.    .margin-transition{

  10.      /* margin-left: 0; */

  11.      background: rgba(0,0,255,0.3);

  12.      transition: margin-left 1s;

  13.    }

  14.    .transform-transition{

  15.      /* transform: translate(0,0); */

  16.      background: rgba(0,255,0,0.3);

  17.      transition: transform 1s;

  18.    }

  19.    .common{

  20.      height: 300px;

  21.      width: 300px;

  22.    }

  23.  </style>

  24. </head>

  25. <body>

  26.  <div class="margin-transition common" id="marginTransition">

  27.    <p>transition:margin-left 1s</p>

  28.  </div>

  29.  <div class="transform-transition common" id="transformTransition">

  30.      <p>transition:tranform 1s</p>

  31.  </div>

  32.  <button id="control">见证奇迹</button>

  33.  <script>

  34.      var btn = document.getElementById('control');

  35.      var marginTransition = document.getElementById('marginTransition');

  36.      var transformTransition = document.getElementById('transformTransition');

  37.      btn.addEventListener("click",function(){

  38.        console.log(marginTransition.style,transformTransition.style)

  39.        marginTransition.style.marginLeft = "500px";

  40.        transformTransition.style.transform = "translate(500px,0)"

  41.      })

  42.  </script>  

  43. </body>

  44. </html>

我将主要借助chrome devtools的performance工具对比两者的性能差别。

先来看margin动画,动态修改DOM节点的margin-left值从0到500px;。

  1. transition: margin-left 1s;

再来看下transform动画,动态修改DOM节点的transform值从translate(0,0)到translate(500px,0)。

  1. transition: transform 1s;

可能图片不是很好地能说明性能差别,那么咱们来列一张耗时对比表,方便咱们计算。

耗时 margin transform
Summery 3518ms 2286ms
Scripting 1.8ms 2.9ms
Rendering 22.5ms 6.9ms
Painting 9.7ms 1.6ms
Other 39.3ms 25.2ms
Idle( browser is waiting on the CPU or GPU to do some processing) 3444.4ms 2249.8ms
GPU使用率 4.1MB 1.7MB

经过上表咱们能够计算出明margin,transform与transition组合实现CSS3动画效果时的性能差别参数。

关键性能参数 margin transform
实际动画耗时(总时间 减去 空闲时间) 73.6ms 36.2ms

计算得出,transform动画耗时约等于margin动画耗时的0.49倍,性能优化50%。

因为我对Other的所作的具体事情不是很清楚,因此这里的实际动画时间也有可能还要减掉Other中的时间,下表是咱们减掉后的数据。

关键性能参数 margin transform
实际动画耗时(总时间 减去 其余时间和空闲时间) 34.3ms 11ms

计算得出,transform动画耗时约等于margin动画耗时的0.32倍,性能优化接近70%。

也就是说,不管咱们减去仍是不减去Other的时间,咱们采用transform实现动画的方式都比margin动画快。

不精确的得出一个小结论:transform比margin性能好50%~70%

虽然会有50%~70%的性能提高,可是须要注意硬件差别,硬件好的状况下可能不能发现卡顿或者其余的一些性能上的问题。

例如在开发小程序的过程当中,模拟器是位于desktop端的,所以它的硬件性能性能更好,例如CPU,GPU。可是一旦在mobile端运行,例如ios或者android上运行时,就可能会出现性能问题,这就是由于移动端的硬件条件逊于PC端致使的。

因此说,性能问题是一直存在的,只不过硬件差别会致使性能影响的程度不一样。

因此咱们再次回过头来,总结出css3动画卡顿的解决方案:

在使用css3 transtion作动画效果时,优先选择transform,尽可能不要使用height,width,margin和padding。

相关文章
相关标签/搜索