前端性能优化(CSS动画篇)

正巧看到在送书,因而乎找了找本身博客上记录过的一些东西来及其无耻的蹭书了~~~html

小广告:更多内容能够看个人博客html5

最近拜读了一下html5rocks上几位大神写的一篇关于CSS3动画性能优化的文章,学到了不少,在这里记录一下,其中的知识都是来源于这俩篇文章,我只是截取了其中比较关注的内容出来,原文地址High Performance AnimationsAccelerated Rendering in Chromeweb

原理

现代浏览器在使用CSS3动画时,如下四种情形绘制的效率较高,分别是:
* 改变位置
* 改变大小
* 旋转
* 改变透明度canvas

层?重绘?回流和重布局?图层重组?

首先要了解CSS的图层的概念(Chrome浏览器)浏览器

浏览器在渲染一个页面时,会将页面分为不少个图层,图层有大有小,每一个图层上有一个或多个节点。在渲染DOM的时候,浏览器所作的工做其实是:
1. 获取DOM后分割为多个图层
2. 对每一个图层的节点计算样式结果(Recalculate style--样式重计算)
3. 为每一个节点生成图形和位置(Layout--回流和重布局)
4. 将每一个节点绘制填充到图层位图中(Paint Setup和Paint--重绘)
5. 图层做为纹理上传至GPU
6. 符合多个图层到页面上生成最终屏幕图像(Composite Layers--图层重组)性能优化

Chrome中知足如下任意状况就会建立图层:
* 3D或透视变换(perspective transform)CSS属性
* 使用加速视频解码的<video>节点
* 拥有3D(WebGL)上下文或加速的2D上下文的<canvas>节点
* 混合插件(如Flash)
* 对本身的opacity作CSS动画或使用一个动画webkit变换的元素
* 拥有加速CSS过滤器的元素
* 元素有一个包含复合层的后代节点(一个元素拥有一个子元素,该子元素在本身的层里)
* 元素有一个z-index较低且包含一个复合层的兄弟元素(换句话说就是该元素在复合层上面渲染)多线程

须要注意的是,若是图层中某个元素须要重绘,那么整个图层都须要重绘。好比一个图层包含不少节点,其中有个gif图,gif图的每一帧,都会重回整个图层的其余节点,而后生成最终的图层位图。因此这须要经过特殊的方式来强制gif图属于本身一个图层(translateZ(0)或者translate3d(0,0,0)),CSS3的动画也是同样(好在绝大部分状况浏览器本身会为CSS3动画的节点建立图层)ide

层和CSS动画

简化一下上述过程,每一帧动画浏览器可能须要作以下工做:
1. 计算须要被加载到节点上的样式结果(Recalculate style--样式重计算)
2. 为每一个节点生成图形和位置(Layout--回流和重布局)
3. 将每一个节点填充到图层中(Paint Setup和Paint--重绘)
4. 组合图层到页面上(Composite Layers--图层重组)布局

若是咱们须要使得动画的性能提升,须要作的就是减小浏览器在动画运行时所须要作的工做。最好的状况是,改变的属性仅仅印象图层的组合,变换(transform)和透明度(opacity)就属于这种状况性能

现代浏览器如Chrome,Firefox,Safari和Opera都对变换和透明度采用硬件加速,但IE10+不是很肯定是否硬件加速

触发重布局的属性

有些节点,当你改变他时,会须要从新布局(这也意味着须要从新计算其余被影响的节点的位置和大小)。这种状况下,被影响的DOM树越大(可见节点),重绘所须要的时间就会越长,而渲染一帧动画的时间也相应变长。因此须要尽力避免这些属性

一些经常使用的改变时会触发重布局的属性:
盒子模型相关属性会触发重布局:
* width
* height
* padding
* margin
* display
* border-width
* border
* min-height

定位属性及浮动也会触发重布局:
* top
* bottom
* left
* right
* position
* float
* clear

改变节点内部文字结构也会触发重布局:
* text-align
* overflow-y
* font-weight
* overflow
* font-family
* line-height
* vertival-align
* white-space
* font-size

这么多经常使用属性都会触发重布局,能够看到,他们的特色就是可能修改整个节点的大小或位置,因此会触发重布局

别使用CSS类名作状态标记

若是在网页中使用CSS的类来对节点作状态标记,当这些节点的状态标记类修改时,将会触发节点的重绘和重布局。因此在节点上使用CSS类来作状态比较是代价很昂贵的

触发重绘的属性

修改时只触发重绘的属性有:
* color
* border-style
* border-radius
* visibility
* text-decoration
* background
* background-image
* background-position
* background-repeat
* background-size
* outline-color
* outline
* outline-style
* outline-width
* box-shadow

这样能够看到,这些属性都不会修改节点的大小和位置,天然不会触发重布局,可是节点内部的渲染效果进行了改变,因此只须要重绘就能够了

手机就算重绘也很慢

在重绘时,这些节点会被加载到GPU中进行重绘,这对移动设备如手机的影响仍是很大的。由于CPU不如台式机或笔记本电脑,因此绘画巫妖的时间更长。并且CPU与GPU之间的有较大的带宽限制,因此纹理的上传须要必定时间

触发图层重组的属性

透明度居然不会触发重绘?

须要注意的是,上面那些触发重绘的属性里面没有opacity(透明度),很奇怪不是吗?实际上透明度的改变后,GPU在绘画时只是简单的下降以前已经画好的纹理的alpha值来达到效果,并不须要总体的重绘。不过这个前提是这个被修改opacity自己必须是一个图层,若是图层下还有其余节点,GPU也会将他们透明化

强迫浏览器建立图层

在Blink和WebKit的浏览器中,一当一个节点被设定了透明度的相关过渡效果或动画时,浏览器会将其做为一个单独的图层,但不少开发者使用translateZ(0)或者translate3d(0,0,0)去使浏览器建立图层。这种方式能够消除在动画开始以前的图层建立时间,使得动画尽快开始(建立图层和绘制图层仍是比较慢的),并且不会随着抗锯齿而导出突变。不过这种方法须要节制,不然会由于建立过多的图层致使崩溃

Chrome中的抗锯齿

Chrome中,非根图层以及透明图层使用grayscale antialiasing而不是subpixel antialiasing,若是抗锯齿方法变化,这个效果将会很是显著。若是你打算预处理一个节点而不打算等到动画开始,能够经过这种强迫浏览器建立图层的方式进行

transform变换是你的选择

咱们经过节点的transform能够修改节点的位置、旋转、大小等。咱们日常会使用lefttop属性来修改节点的位置,但正如上面所述,lefttop会触发重布局,修改时的代价至关大。取而代之的更好方法是使用translate,这个不会触发重布局

JS动画和CSS3动画的比较

咱们常常面临一个抉择:是使用JavaScript的动画仍是使用CSS的动画,下面将对比一下这两种方式

JS动画

缺点:JavaScript在浏览器的主线程中运行,而其中还有不少其余须要运行的JavaScript、样式计算、布局、绘制等对其干扰。这也就致使了线程可能出现阻塞,从而形成丢帧的状况。

优势:JavaScript的动画与CSS预先定义好的动画不一样,能够在其动画过程当中对其进行控制:开始、暂停、回放、停止、取消都是能够作到的。并且一些动画效果,好比视差滚动效果,只有JavaScript可以完成

CSS动画

缺点:缺少强大的控制能力。并且很难以有意义的方式结合到一块儿,使得动画变得复杂且易于出问题。
优势:浏览器能够对动画进行优化。它必要时能够建立图层,而后在主线程以外运行。

前瞻

Google目前正在探究经过JS的多线程(Web Workers)来提供更好的动画效果,而不会触发重布局及样式重计算

结论

动画给予了页面丰富的视觉体验。咱们应该尽力避免使用会触发重布局和重绘的属性,以避免失帧。最好提早申明动画,这样能让浏览器提早对动画进行优化。因为GPU的参与,如今用来作动画的最好属性是以下几个:
* opacity
* translate
* rotate
* scale

也许会有一些新的方式使得可使用JavaScript作出更好的没有限制的动画,并且不用担忧主线程的阻塞问题。但在那以前,仍是好好考虑下如何作出流畅的动画吧

相关文章
相关标签/搜索