浏览器渲染之什么是合成层?
居中为什么要使用 transform , 为什么不使用 margin Left/Top ?
在我们日常开发中经常会实现一些动画,有时候我们可能会选择改变 top/left
去实现,那么这个节点的渲染会发生在普通文档流中。该属性的变化会导致重排(reflow/relayout
)
而使用 transform
和 opacity
实现动画能够让节点被放置到一个独立合成层中进行渲染绘制,动画不会影响其他图层,并且 GPU
渲染相比 CPU
能够更快,这会让你的动画变的更加流畅
什么是合成层?
在讨论合成层
之前我们先简单了解一下浏览器渲染,浏览器常见的渲染引擎有 Webkit/Gecko 等,他们的主要渲染流程基本相同
讨论一下 WebKit 简化的渲染流程。
- 浏览器下载并解析 HTML。
- 处理
CSS
构建CSSOM
树,生成DOM
树。 DOM
与CSSOM
合并成一个Render
树。- 有了
Render Tree
,浏览器可以知道各个节点的CSS
定义以及他们的从属关系,从而去计算出每个节点在屏幕中的位置,生成一个足够大的画布来容纳所有元素。 - 根据浏览器提供各层的信息合成图层,显示到屏幕上。
合成层
就出现在最后一步流程中,这些合成图层中一些特殊的图层被认为是合成层(Compositing Layers)
首先合成就是将页面的各个部分分成多个层、单独光栅化它们并在合成器线程中合成为一个页面的技术。
光栅化 : 浏览器根据文档的结构、每个元素的样式、页面的几何形状和绘制顺序转换为屏幕上的像素的过程
- 一般来说,拥有一些特定属性的渲染层,会被浏览器自动提升为合成层。
- 合成层拥有单独的图层(GraphicsLayer),和其他图层之间无不影响。
- 而其它不是合成层的渲染层,则和第一个拥有图层的父层共用一个,也就是普通文档流中的内容
提升为合成层的属性
我们看一些常见的提升为合成层的属性。
- 设置
transform: translateZ(0)
,注意它必须是translateZ
,因为它使用GPU
来计算perspective distortion
(透视失真)。perspective
在3D
设计中是一个重要的属性。如果你使用translateX
或translateY
,元素将会被绘制在普通文档流中 backface-visibility: hidden
: 指定当元素背面朝向观察者时是否可见transform
/opacity
video
、canvas
、iframe
等元素。will-change
: 告诉浏览器该元素会有哪些变化,这样浏览器可以提前做好对应的优化准备工作。- 当该属性的值为
opacity
、transform
、top
、left
、bottom
、right
时。
- 当该属性的值为
隐式合成就是特定场景下,存在会被默认提升为合成层的情况。
- 一个或多个非合成元素应出现在堆叠顺序上 (
z-index
) 的合成元素之上,会被提升为合成层。
合成层的好处和问题
合成层开启的好处
开启硬件加速,合成层的位图会交由
GPU
合成,相比CPU
处理要快。合成层发生
repaint
的时候,不会影响其他图层。对于
transform
和opacity
效果,不会触发layout
和paint
。
当然合成层也存在一些问题:
- 如果我们把所有渲染工作都交给
GPU
,在现有的优化下,它会导致渲染内存占用比大幅度提升,反而出现负面的效果。 - 另外隐式合成容易产生大量我们意料之外的合成层,过大的内存占用,会让页面变的卡顿,性能优化适得其反。
谨慎使用 will-change
除非你的元素的真的存在某个属性马上会发生变化,例如 transform
,你可以使用 will-change: transform
告知浏览器,根据您打算更改的元素,浏览器可能可以预先安排,元素的改变和渲染速度都会变得更快。
这些属性可能会给你带来一些副作用
但需要注意的是,不要创建太多的渲染层。因为每创建一个新的渲染层,就意味着新的内存分配和更复杂的层的管理。
如果你已经把一个元素放到一个新的合成层里,那么可以使用 Timeline 来确认这么做是否真的改进了渲染性能。别盲目提升合成层,一定要分析其实际性能表现。
其实从性能方面考虑,最理想的渲染流水线是没有布局和绘制环节的,只需要做合成层的合并即可:
为了实现上述效果,就需要只使用那些仅触发 Composite
的属性。目前,只有两个属性是满足这个条件的:transforms 和 opacity。
合理管理合成层:
大多数人都很喜欢使用
translateZ(0)
来进行所谓的硬件加速,以提升性能但是性能优化并没有所谓的”银弹” (可解决复杂问题的简单方案),
translateZ(0)
不是,本文列出的优化建议也不是。抛开了对页面的具体分析,任何的性能优化都是站不住脚的,盲目的使用一些优化措施,结果可能会适得其反。
- 创建一个新的合成层并不是免费的,它得消耗额外的内存和管理资源。实际上,在内存资源有限的设备上,合成层带来的性能改善,可能远远赶不上过多合成层开销给页面性能带来的负面影响。因此切实的去分析页面的实际性能表现,不断的改进测试,才是正确的优化途径。