对页面中 DOM 元素的绘制是在多个层上进行的。在每个层上完成绘制过程之后,浏览器会将所有层按照合理的顺序合并成一个图层,然后显示在屏幕上。对于有位置重叠的元素的页面,这个过程尤其重要,因为一旦图层的合并顺序出错,将会导致元素显示异常。
DOM 树中每个节点都会对应一个 LayoutObject
,LayoutObject
知道如何在屏幕上 paint Node 的内容。当LayoutObject
处于相同的坐标空间时,就会形成一个RenderLayers
(PaintLayer
) ,也就是渲染层。PaintLayer 最初是用来实现 stacking contest(层叠上下文),以此来保证页面元素以正确的顺序合成(composite),这样才能正确的展示元素的重叠以及半透明元素等等。因此满足形成层叠上下文条件的 LayoutObject 一定会为其创建新的渲染层。
创建PaintLayer
常见的 3 类:
- NormalPaintLayer
- 根元素(HTML)
- 有明确的定位属性(relative、fixed、sticky、absolute)
- 透明的(opacity 小于 1)
- 有 CSS 滤镜(fliter)
- 有 CSS mask 属性
- 有 CSS mix-blend-mode 属性(不为 normal)
- 有 CSS transform 属性(不为 none)
- backface-visibility 属性为 hidden
- 有 CSS reflection 属性
- 有 CSS column-count 属性(不为 auto)或者 有 CSS column-width 属性(不为 auto)
- 当前有对于 opacity、transform、fliter、backdrop-filter 应用动画
- OverflowClipPaintLayer(overflow 不为 visible)
- NoPaintLayer(不需要 paint 的 PaintLayer,比如一个没有视觉属性(背景、颜色、阴影等)的空 div)
满足以上条件的 LayoutObject 会拥有独立的渲染层,而其他的 LayoutObject 则和其第一个拥有渲染层的父元素共用一个
从 PaintLayers 到 GraphicsLayers
某些特殊的渲染层会被认为是合成层(Compositing Layers),合成层拥有单独的 GraphicsLayer。而其他不是合成层的渲染层,则和其第一个拥有 GraphicsLayer 父层公用一个。
渲染层提升为合成层的原因有一下几种:
RenderLayers
(PaintLayer
) 来保证页面元素以正确的顺序合成,这时候就会出现层合成(composite)
- RenderLayers 渲染层,这是负责对应 DOM 子树
- GraphicsLayers 图形层,这是负责对应 RenderLayers 子树
每个 GraphicsLayer 都有一个 GraphicsContext,GraphicsContext 会负责输出该层的位图,位图是存储在共享内存中,作为纹理上传到 GPU 中,最后由 GPU 将多个位图进行合成,然后 draw 到屏幕上,此时,我们的页面也就展现到了屏幕上
渲染层提升为合成层的原因有一下几种:
直接原因
- 硬件加速的 iframe 元素
- video 元素
- 覆盖在 video 元素上的视频控制栏
- 3D Canvas 提升为合成层
- 3D transform
- backface-visibility: hidden
- 对 opacity、transform、fliter、backdropfilter 应用了 animation 或者 transition(需要是 active 的 animation 或者 transition,当 animation 或者 transition 效果未开始或结束后,提升合成层也会失效)
- will-change 设置为 opacity、transform、top、left、bottom、right(其中 top、left 等需要设置明确的定位属性,如 relative 等)
后代元素原因
有合成层后代同时本身有 transform、opactiy(小于 1)、mask、fliter、reflection 属性