CSS3开启硬件加速及利弊
最近了解了一下用css3开启硬件加速的这个功能,不得不感叹浏览器这些东西太神奇了,要不是师兄提起,我根本就不知道居然有这种东西。所以还是要提高一下自己的信息来源渠道的。
巴拉巴拉了一下,下面我们正式来看下css3是如何开启硬件加速的:
其实,所谓的加速,就是浏览器中用css开启硬件加速,使GPU (Graphics Processing Unit) 发挥功能的一系列活动。
(写在前面重点之中的重点,建议在动画或者是使用比较多变化的网页使用这个技巧,如果是一般普通网页,建议不要使用,或者是慎用。这个我后面会提到原因)
举个例子:
CSS的 animations, transforms 以及 transitions 不会自动开启GPU加速,而是由浏览器的缓慢的软件渲染引擎来执行。这个时候或许就需要开启硬件加速功能~\(≧▽≦)/~。那我们怎样才可以切换到GPU模式呢,很多浏览器提供了某些触发的CSS规则。
现在,像Chrome, FireFox, Safari, IE9+和最新版本的Opera都支持硬件加速,当它们检测到页面中某个DOM元素应用了某些CSS规则时就会开启,最显著的特征的元素的3D变换。
.example{ -webkit-transform: translate3d(250px,250px,250px) rotate3d(250px,250px,250px,-120deg) scale3d(0.5, 0.5, 0.5); }
如果我们并没有用到这些功能呢?没有关系啊,我们可以设置一个‘空’值,也就是让其在页面的效果是--- 无效果的,这样就欺骗了浏览器,让他开启了硬件加速。
.example { -webkit-transform: translateZ(0); -moz-transform: translateZ(0); -ms-transform: translateZ(0); -o-transform: translateZ(0); transform: translateZ(0); /* Other transform properties here */ }
这个时候或许你又可能会发现有点问题,页面可能会出现闪烁的效果,(可能是浏览器自带的bug?有待研究,如果有知道的朋友欢迎科普)那么我们可以用一下方式来修复:
.example { -webkit-backface-visibility: hidden; -moz-backface-visibility: hidden; -ms-backface-visibility: hidden; backface-visibility: hidden; -webkit-perspective: 1000; -moz-perspective: 1000; -ms-perspective: 1000; perspective: 1000; /* Other transform properties here */ }
如果是webkit内核,还有一种方式可以解决:
.example { -webkit-transform: translate3d(0, 0, 0); -moz-transform: translate3d(0, 0, 0); -ms-transform: translate3d(0, 0, 0); transform: translate3d(0, 0, 0); /* Other transform properties here */ }
GPU固然加速了网页,但是同时它增加了内存的使用,实际上是可能会导致严重的性能问题,如果是移动设备,它会减少移动端设备的电池寿命。
last but no least!!!! CSS3硬件加速也有坑!!!
在Webkit内核的浏览器中,硬件加速会把需要渲染的元素放到特定的『Composited Layer』中,表示放到了一个新的『复合层(composited layer)』中渲染。
那我们怎么知道如何才能知道是哪部分被放到了复合层呢?
在chrome的控制台可以这样开启:
打开了,那么我们要怎么判断呢?
此处附上一个地址,是一个css3动画库(animate.css)打开它~~
这个时候他是这样子的,然后随便选择一个动画,这里我闭着眼睛选了一个比较长的动画,方便我截图,为了方便观察,这次我保证不会在截图里面乱圈东西
这个时候,Animate.css标题出现了不一样颜色的框框!!!!里面分两种颜色:蓝色和黄色
蓝色的细线是浏览器渲染时候的『瓦片』,浏览器绘制页面的时候只会绘制可视区域一定范围内的瓦片,以节省性能开销,而黄色的边框框起来的,就代表了这个元素被放到特殊的复合层中渲染,跟主文档不在一个层中
下面我引用了一个大神的文章:出处 http://www.th7.cn/web/html-css/201509/121970.shtml
(这个大神看自己的项目,发现基本所有都用了3D加速,下面用斜体表示引用)
简化代码,很快就发现,原来罪魁祸首在这里:
头部的那个轮播动画元素的存在居然会导致下面所有相对和绝对定位的元素都被放到复合层中。。。
查了一些 资料 :
层创建标准
什么情况下能使元素获得自己的层?虽然 Chrome 的启发式方法(heuristic)随着时间在不断发展进步,但是从目前来说,满足以下任意情况便会创建层:
3D 或透视变换(perspective transform) CSS 属性 使用加速视频解码的 元素 拥有 3D (WebGL) 上下文或加速的 2D 上下文的 元素 混合插件(如 Flash) 对自己的 opacity 做 CSS 动画或使用一个动画 webkit 变换的元素 拥有加速 CSS 过滤器的元素 元素有一个包含复合层的后代节点(换句话说,就是一个元素拥有一个子元素,该子元素在自己的层里) 元素有一个 z-index 较低且包含一个复合层的兄弟元素(换句话说就是该元素在复合层上面渲染)
主要是最后一条,我觉得它的中文翻译不是很准确,原文其实是:
Element has a sibling with a lower z-index which has a compositing layer (in other words the it’s rendered on top of a composited layer)
这句话的意思是,如果有一个元素,它的兄弟元素在复合层中渲染,而这个兄弟元素的z-index比较小,那么这个元素(不管是不是应用了硬件加速样式)也会被放到复合层中。
最可怕的是,浏览器有可能给复合层之后的所有相对或绝对定位的元素都创建一个复合层来渲染,于是就有了上面我厂项目截图的那种效果。我们之前一直追查为什么这个页面多了一个list之后在安卓下滚动会变得非常卡,最终被确定就是这个问题了!
于是乎我写了一个页面,让大家看看这东西到底有多大威力:
我在上面这个页面中放置了一个h1标题,应用了translate3d动画,使得它被放到composited layer中渲染,然后在这个元素后面创建了2000个list,每个list中都有一个图片,一个标题和一个日期显示,其中图片和日期显示是绝对定位,父容器li是相对定位,然后,各位可以按照前述的说明打开chrome的『show composited layer borders』选项看看这个页面的内容复合层分布:
然后我写了一个简单的滚动条移动操作:
setInterval('document.body.scrollTop++', 0);
然后用timeline抓一下页面性能:
一次『Composite Layers』的计算居然要 96.206 ms !!这还是在我的mac系统上哦,手机上真的会卡出翔。
我在页面上放置了一个开关『为动画元素设置z-index』,这个checkbook点击之后,会用js给那个动画的h1元素加 position:relative 和 z-index: 1 ,这种做法的原理是人为提升动画元素的z-index,让浏览器知道这个元素的层排序,就不会很傻逼的把其他z-index比它高的元素也弄到复合层中了,看看这个效果:
仅仅给动画元素设置一个高一些的z-index,就能解决这种无厘头增加复合层的问题。再用滚动条移动函数抓一下页面性能:
完全恢复正常了!
大家可以用支持『硬件加速』的『安卓』手机浏览器测试上述页面,给动画元素加z-index前后的性能差距非常明显。
不过也不是所有浏览器都有这个问题,我在mac上的Safari、firefox都没有明显差异,安卓手机上的QQ浏览器好像也正常,猎豹、UC、欧朋、webview等浏览器差距明显。
好了最后总结一下:
使用3D硬件加速提升动画性能时,最好给元素增加一个z-index属性,人为干扰复合层的排序,可以有效减少chrome创建不必要的复合层,提升渲染性能,移动端优化效果尤为明显。
大家可以现在就排查一下这类问题,尤其是用了轮播、动画loading的页面,出现这问题很常见。另外推荐在追查性能问题的时候打开『show composited layer borders』选项,如果页面有很多黄色的框肯定是不对的。