高性能Web动画和渲染原理系列(5)合成层的生成条件和陷阱

示例代码托管在:http://www.github.com/dashnowords/blogs前端

博客园地址:《大史住在大前端》原创博文目录git

华为云社区地址:【你要的前端打怪升级指南】github

一. 硬件加速相关的几个概念

以前介绍到了RenderLayer渲染层的概念,在涉及到硬件加速的话题时,出现了不少新的概念,参考《Webkit技术内幕》一书的介绍总结以下:后端

Webkit决定将哪些RenderLayer对象组合在一块儿,造成一个有后端存储的新层,这一新层不久后会用于合成,这里称之为合成层CompositingLayer)。每个合成层都会对应一个或多个后端存储,由RenderLayerBacking类进行统一管理,后端存储空间使用GraphicsLayer来表示,也就是说RenderLayerBacking管理着一个或多个与对应的合成层有关的GraphicsLayer浏览器

笔者旁白:对于渲染过程来讲,只须要理解这里造成了新的CompositingLayer合成层就能够了,其余的层概念基本都是用于实现对CompositingLayer功能支持的,概念数量太多对于理解宏观流程是一大障碍。ide

二. 合成层的生成条件

显式提高

合成层的处理是依赖于硬件加速的,可是GPU的存储空间有限最好不要滥用,过多的合成层有可能还会形成相反的效果,因此浏览器只会将知足下列任意条件的RenderLayer提高为CompositingLayer布局

  • 具备CSS3D属性或CSS透视效果
  • 包含的RenderObject节点表示的是使用硬件加速的视频解码技术的HTML5video元素
  • 包含的RenderObject节点包含使用了硬件加速的Canvas2DWebGL技术
  • 使用了CSS透明效果或CSS变形动画
  • 使用了硬件加速的CSS Filters技术(有的文献中表示filters属性并无提高为合成层的效果,推测只有一部分filters滤镜效果须要使用硬件加速,并不是全部)
  • 使用了剪裁Clip或者反射Reflection,而且它的后代中包含一个合成层
  • 拥有一个Z坐标比本身小的兄弟节点,且该节点是一个合成层。

上面的规则里咱们最熟悉的可能就是transform:translateZ(0)或者在关键帧动画的定义中改变transformopacity属性。固然,随着技术的演进,上面的规则并不必定全面Chromium官网提供的开发者演讲PPT中也对提高的理由进行了相关的描述:测试

你能够在Chrome调试面板的【Layers】功能中对分层相关的结果进行检视,查看哪些层进行了提高以及被提高的具体缘由,避免出现与本身意图相悖的层提高:动画

隐式提高

RenderLayer知足特殊条件时被提高为CompositingLayer对开发者而言是比较可控的。但除此以外,在浏览器的合成阶段,还存在隐式合成的情况,一些特定的场景中出现的合成层并非开发者主观指望的。

隐式合成主要发生在元素出现重叠时,层级较低的元素若是被提高为合成层后,最终合成的结果就可能出如今原来比本身层级更高的元素之上,从而出现错误的堆叠关系,为了纠正这种关系,只能让本来层级高(可是并不用提高为合成层的元素)发生提高也成为合成层。例以下面的代码:

<div style="position:absolute;height:200px;width:200px;background-color: #DA5961;"></div>
<div style="position:absolute;left:30px;top:50px;height:200px;width:200px;background-color: #3498db;"></div>
<div style="position:absolute;left:60px;top:100px;height:200px;width:200px;background-color: #1abc9c;"></div>

三个div盒子堆叠在一块儿,能够看到它们都绘制在同一个层上(这里的层并不与RenderLayer对应,毕竟它只是一个中间态的树结构):

此时若是为最底下的红色矩形添加transform:translateZ(0)属性将其提高为合成层后,为了保证正确的堆叠关系,蓝色和绿色的矩形就会被提高为合成层,代码以下:

<div style="transform:translateZ(0);position:absolute;height:200px;width:200px;background-color: #DA5961;"></div>
<div style="position:absolute;left:30px;top:50px;height:200px;width:200px;background-color: #3498db;"></div>
<div style="position:absolute;left:60px;top:100px;height:200px;width:200px;background-color: #1abc9c;"></div>

蓝色和绿色的矩形并无造成独立的合成层,而是被压缩在同一个合成层中:

从上图中的细节信息中能够看到,提高的缘由是layerFotSquashingContent,也就是为了保证堆叠顺序的正确,用一个单独的合成层来将受到影响的元素收集在一块儿,既保证堆叠顺序,也避免在指望以外生成过多的合成层。若是调整绿色矩形的位置,就能够看到,当视觉上不存在覆盖时,它就不须要提高了:

BUT!!!还没完,最坑的部分来了,若是此时给蓝色的div加上一点动画,你会发现绿色div又被提高到了独立的合成层上,尽管他们之间并无重叠区,但仍是被提高了:

从图中的合成缘由能够看到:它可能和一个相邻的合成层元素发生交叠,因此被提高了。没错,就是“可能”。Fouber这篇CSS硬件加速也有坑中的示例更加详细,子元素引起父元素提高,父元素又引起兄弟元素提高。

三. 硬件加速的权衡

全部的技术方案都是有代价的,这是亘古不变的道理,合成层的好处很明显,GPUCPU的处理速度快不少,触发repaint重绘时,只须要重绘独立的层,而后从新合成便可,不须要重绘整个画面。但它也存在一些弊端:首先是数据传输的问题,CPUGPU的关系就比如客户端和服务端同样,它们的协做是须要传输数据的,当层的数量达到必定量级后,传输的速度就会影响到总体的处理效率,进而致使在一些低中端设备上出现闪烁等现象;另外,每一个合成层都具会占据额外的内存,这个数量一般比开发者觉得的要大的多,尤为是在移动端这种硬件资源受限制的场景中,过量的内存使用分分钟就会让应用崩溃。

四. 动画实现的一些建议

  1. 使用transform实现动画

    这多是咱们编写动画时听到最多的建议了。例如使用lefttop来实现位置动画时,绝对定位的元素会造成RenderLayer,可是却不符合提高为CompositingLayer的条件,因此动画元素就会和Document处在同一个合成层里,持续进行的动画就会致使Document这一层(一般是正常文档流这一层,包含了大量的流式布局的元素)不断重绘,从而影响渲染效率,若是可以让动画的节点放到单独的合成层里,就能够避免这种大规模重绘,并借助GPU加速合成的能力加速整个渲染流程。

  2. 排查被动提高的情形

    被动提高主要是指“兄弟元素相对层级低于本身但倒是一个合成层”的情形以及“发生堆叠遮挡的几个元素中层级较低的元素被提高为合成层”的情况。通常的解决方案是主动提高动画元素的z-index值或者调整文档结构中节点的前后顺序,固然全部的结果都还须要经过测试来确认。

  3. 考虑合成层的空间占用

    合成层的后端存储是渲染后的像素点数据,它的体积可能会很是大,在使用大屏图片时须要尽量将其压缩至视觉可接受的范围而不能一味追求高清,对于纯色的元素,可使用较小的尺寸并借助transform:scale来放大至须要的尺寸。

  4. 实测为王

    任何方案都只是一种思路,必须经过在真实环境测试验证才能确认其有效性。

相关文章
相关标签/搜索