CSS3硬件加速也有坑
2015/09/20 · CSS ·
硬件加速
原文出处: 张云龙(@前端农民工)
常听人说:
移动端要想动画性能流畅,应该使用3d硬件加速
最近深入了解了一些浏览器内核的细节,感觉这里面其实有坑啊。。。
事情要从最近看的《WebKit技术内幕》说起,第二章介绍了网页的结构,其中提到了Webkit硬件加速的方式,会把需要渲染的元素放到特定的『Composited
Layer』中,在chrome的控制台可以这样开启:
选择『Show composited layer
borders』以后,就能看到有动画3d变换的元素会被一个黄色的边框圈起来,表示放到了一个新的『复合层(composited
layer)』中渲染,大概长这个样子:
蓝色的细线是浏览器渲染时候的『瓦片』,浏览器绘制页面的时候只会绘制可视区域一定范围内的瓦片,以节省性能开销,而黄色的边框框起来的,就代表了这个元素被放到特殊的复合层中渲染,跟主文档不在一个层中
然后我觉得这个视图挺有意思的,就拿来看了一下国内某项目,不看不知道,一看被吓尿:
这个项目什么时候搞成所有元素都用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比较小,那么这个元素(不管是不是应用了硬件加速样式)也会被放到复合层中。
www.301.net,最可怕的是,浏览器有可能给复合层之后的所有相对或绝对定位的元素都创建一个复合层来渲染,于是就有了上面那个项目截图的那种效果。之前一直奇怪为什么这个页面滚动很卡,明明没有多少DOM,现在看来问题就在这里了!
于是乎我写了一个页面,让大家看看这东西到底有多大威力:
我在上面这个页面中放置了一个h1标题,应用了translate3d动画,使得它被放到composited
layer中渲染,然后在这个元素后面创建了2000个list,每个list中都有一个图片,一个标题和一个日期显示,其中图片和日期显示是绝对定位,父容器li是相对定位,然后,各位可以按照前述的说明打开chrome的『show
composited layer borders』选项看看这个页面的内容复合层分布:
就是这个鸟样子,很难想象,这样的页面滚动起来会卡成什么样。我用的是mac机器,快速拖动滚动条chrome已经非常吃力了,然后我写了一个简单的滚动条移动操作:
setInterval(‘document.body.scrollTop++’, 0);
然后用timeline抓一下页面性能:
一次『Composite
Layers』的计算居然要 96.206 ms
!!这还是在我的mac系统上哦,手机上真的会卡出翔。
我在页面上放置了一个开关『为动画元素设置z-index』,这个checkbox点击之后,会用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』选项,如果页面有很多黄色的框肯定是不对的。
最后,再次推荐一下《Webkit技术内幕》这本书。浏览器内核之于前端工程师,就如同操作系统之于后端工程师,毕竟是我们程序运行的宿主环境,多了解一些,很多问题容易想通。
1 赞 1 收藏
评论
问题描述:
产生条件:
在360安全浏览器/360极速浏览器(npapi插件开启)
CSS3动画及flash 共存
上述条件同时符合的场景下,在某个flash播放的时候,触发任意一个带有CSS3动画的元素,会发现flash消失,待CSS3动画播放完毕之后重新出现,如果页面比较复杂时会发现其它文本元素也会有闪动现象。(此时需要的字体饱和度(hsla中的s)较高,这样肉眼能够清晰的看到字体闪动。)
环境准备:
1,打开360安全浏览器【其它主流浏览器非NPAPI】
2. 先看下是否use npapi
方法: 在地址栏输入 se://plugins 如图查看是否开启
场景带入:
感谢文章CSS3硬件加速,硬件加速的方式,会把需要渲染的元素放到特定的复合层(composited
layer)中,如果你想查看你的页面是否使用了2D或者3D加速,可以用下列方式去查看。
在控制台可以这样开启:【我以360安全浏览器为例】
勾选Show composited layer
borders以后,就能看到有动画3d/2d变换的元素会被一个黄色的边框圈起来,表示放到了一个新的
“复合层(composited layer)” 中渲染,大概长这个样子:
蓝色的细线是浏览器渲染时候的瓦片,浏览器绘制页面的时候只会绘制可视区域一定范围内的瓦片,以节省性能开销,而黄色的边框框起来的,就代表了这个元素被放到特殊的复合层中渲染,跟主文档不在一个层中,但其实我只有其中的几处使用了2D动画,但显然很多并不会涉及的容器也被黄色的边框框了起来,经过排查发现,是因为CSS3动画【这儿要说一下动画包括
transform 和 animation, 不论是@keyframes 还是简单的 translate rotate …
】导致之后所有绝对相对定位的元素都被放到复合层中渲染。
查了一些 资料:
层创建标准什么情况下能使元素获得自己的层?满足以下任意情况便会创建层:
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)
我回过头看了下我的页面,非常符合以上的条件,所以很容易被创建了复合层,导致怀疑
CSS3 3D加速的某些属性影响了页面中的内容的错觉
直至到后期flash动画与CSS3动画同时存在的场景里的时候 才得以解决。
解决方法:
1.
如果不存在flash,只有CSS3动画,影响到了页面其它例如字体抖动的情况下:
需要给动画元素加 position:relative和 z-index:
1,这种做法的原理是人为提升动画元素的z-index,让浏览器知道这个元素的层排序,就不会把其他z-index比它高的元素也弄到复合层中了;
2.
如果flash和CSS3动画并存,影响到了页面其它例如字体抖动的情况或者CSS3动画影响到了正在播放的flash的时候
:
需要动画元素加 position:relative和 z-index: 1,注意
一定要动画元素的z-index高于flash的z-index;
注:flash在制作的时候如果加了滤镜效果,在360安全浏览器【npapi】下,会有黑底出现,极其影响视觉感受。
动画卡顿是在移动web开发时经常遇到的问题,解决这个问题一般会用到css3硬件加速。
css3硬件加速这个名字感觉上很高大上,其实它做的事情可以简单概括为:通过GPU进行渲染,解放cpu。
原理
DOM树和CSS结合后形成渲染树。渲染树中包含了大量的渲染元素,每一个渲染元素会被分到一个图层中,每个图层又会被加载到GPU形成渲染纹理。GPU中transform是不会触发
repaint 的,这一点非常类似3D绘图功能,最终这些使用
transform的图层都会由独立的合成器进程进行处理。
通过chrome的timeline工具,绿色框代表需要repaint的部分,橙色框代表渲染图层,对比两者可知采用的css3硬件加速后,不会进行repaint操作,而只会产生一个渲染图层,GPU就负责操作这个渲染图层。
不使用css3加速.png
使用css3加速.png
参考文章:
- CSS动画之硬件加速:作者比较详细介绍了硬件加速,总结的很到位。
- CSS3硬件加速也有坑:
作者介绍了由于z-index造成复合图层过多,以后开发加以注意。 - 使用CSS3
will-change提高页面滚动、动画等渲染性能:
will-change的介绍 - javascript性能优化-repaint和reflow:性能杀手以及优化方法。
- 两张图解释CSS动画的性能:比较详细对比了采用硬件加速和不采用硬件加速的流程差异。
复合图层
在原理中我们提到transform会创建一个图层,GPU会来执行transform的操作,这个图层且称为复合图层(composited
layer)。
虽然 Chrome
的启发式方法(heuristic)随着时间在不断发展进步,但是从目前来说,满足以下任意情况便会创建层:
- 3D 或透视变换(perspective transform) CSS 属性
- 使用加速视频解码的元素,如<video>
- 拥有 3D (WebGL) 上下文或加速的 2D 上下文的元素,如<canvas>
- 混合插件(如 Flash)
- 对自己的 opacity 做 CSS 动画或使用一个动画 webkit 变换的元素
- 拥有加速 CSS 过滤器的元素,如CSS filters
- 元素有一个包含复合层的后代节点(换句话说,就是一个元素拥有一个子元素,该子元素在自己的层里)
- 元素有一个 z-index
较低且包含一个复合层的兄弟元素(换句话说就是该元素在复合层上面渲染)
如果页面建立了过多的复合图层,同样也会造成页面的卡顿。在CSS3硬件加速也有坑这篇文章中,作者介绍了由于z-index造成复合图层过多而引发的问题,在以后开发时可以借鉴。可以调试图层过多卡顿页面了解z-idnex对图层创建的影响。
现象
通过对比不使用css3加速和使用css3加速两个例子,我们可以看到两者渲染的差异:
不使用css3加速.png
使用css3加速.png
前者通过改变top和left
属性进行动画,fps维持在47左右,cpu一直进行计算;后者通过transform
实现,fps在62左右,cpu基本不需要计算。对比可知通过transform
不仅提升了渲染性能,也解放了cpu。
注意事项
- 不能让每个元素都启用硬件加速,这样会暂用很大的内存,使页面会有很强的卡顿感。
- GPU渲染会影响字体的抗锯齿效果。这是因为GPU和CPU具有不同的渲染机制,即使最终硬件加速停止了,文本还是会在动画期间显示得很模糊。
启用
如下几个css属性可以触发硬件加速:
- transform
- opacity
- filter
- will-change:哪一个属性即将发生变化,进而进行优化。
上面的的例子中用到的是transform
2D,它是在运行时才会创建图层,因此在动画开始和结束时会进行repaint操作;而浏览器在渲染前就为transform
3D创建了图层。
开始时的repaint
结束时的repaint
可以通过transform的3D属性强制开启GPU加速:
transform: translateZ(0);
transform: rotateZ(360deg);