JavaScript:一般来说,我们会使用 JavaScript 来实现一些视觉变化的效果。比如做一个动画或者往页面里添加一些 DOM 元素等。 Style:计算样式,这个过程是根据 CSS 选择器,对每个 DOM 元素匹配对应的 CSS 样式。这一步结束之后,就确定了每个 DOM 元素上该应用什么 CSS 样式规则。 Layout:布局,上一步确定了每个 DOM 元素的样式规则,这一步就是具体计算每个 DOM 元素最终在屏幕上显示的大小和位置。web 页面中元素的布局是相对的,因此一个元素的布局发生变化,会联动地引发其他元素的布局发生变化。比如, 元素的宽度的变化会影响其子元素的宽度,其子元素宽度的变化也会继续对其孙子元素产生影响。因此对于浏览器来说,布局过程是经常发生的。 Paint:绘制,本质上就是填充像素的过程。包括绘制文字、颜色、图像、边框和阴影等,也就是一个 DOM 元素所有的可视效果。一般来说,这个绘制过程是在多个层上完成的。 Composite:渲染层合并,由上一步可知,对页面中 DOM 元素的绘制是在多个层上进行的。在每个层上完成绘制过程之后,浏览器会将所有层按照合理的顺序合并成一个图层,然后显示在屏幕上。对于有位置重叠的元素的页面,这个过程尤其重要,因为一旦图层的合并顺序出错,将会导致元素显示异常。
当然,本文我们只来关注 Composite 部分。
浏览器渲染原理
在讨论 Composite 之前,有必要先简单了解下一些浏览器(本文只是针对 Chrome 来说)的渲染原理,方便对之后一些概念的理解。更多详细的内容可以参阅 GPU Accelerated Compositing in Chrome
注:由于 Chrome 对 Blank 引擎某些实现的修改,某些我们之前熟知的类名有了变化,比如 RenderObject 变成了 LayoutObject,RenderLayer 变成了 PaintLayer。感兴趣的看以参阅 Slimming Paint 。
在浏览器中,页面内容是存储为由 Node 对象组成的树状结构,也就是 DOM 树。每一个 HTML element 元素都有一个 Node 对象与之对应,DOM 树的根节点永远都是 Document Node。这一点相信大家都很熟悉了,但其实,从 DOM 树到最后的渲染,需要进行一些转换映射。
从 Nodes 到 LayoutObjects
DOM 树中得每个 Node 节点都有一个对应的 LayoutObject 。LayoutObject 知道如何在屏幕上 paint Node 的内容。
从 LayoutObjects 到 PaintLayers
一般来说,拥有相同的坐标空间的 LayoutObjects,属于同一个渲染层(PaintLayer)。PaintLayer 最初是用来实现 stacking contest(层叠上下文) ,以此来保证页面元素以正确的顺序合成(composite),这样才能正确的展示元素的重叠以及半透明元素等等。因此满足形成层叠上下文条件的 LayoutObject 一定会为其创建新的渲染层,当然还有其他的一些特殊情况,为一些特殊的 LayoutObjects 创建一个新的渲染层,比如 overflow != visible 的元素。根据创建 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 不为 visibleNoPaintLayer
不需要 paint 的 PaintLayer,比如一个没有视觉属性(背景、颜色、阴影等)的空 div。满足以上条件的 LayoutObject 会拥有独立的渲染层,而其他的 LayoutObject 则和其第一个拥有渲染层的父元素共用一个。
从 PaintLayers 到 GraphicsLayers
某些特殊的渲染层会被认为是合成层(Compositing Layers),合成层拥有单独的 GraphicsLayer,而其他不是合成层的渲染层,则和其第一个拥有 GraphicsLayer 父层公用一个。
每个 GraphicsLayer 都有一个 GraphicsContext,GraphicsContext 会负责 输出该层的位图,位图是存储在共享内存中,作为纹理上传到 GPU 中,最后由 GPU 将多个位图进行合成,然后 draw 到屏幕上,此时,我们的页面也就展现到了屏幕上。
渲染层提升为合成层的原因有一下几种:
注:渲染层提升为合成层有一个先决条件,该渲染层必须是 SelfPaintingLayer(基本可认为是上文介绍的 NormalPaintLayer)。以下所讨论的渲染层提升为合成层的情况都是在该渲染层为 SelfPaintingLayer 前提下的。
直接原因(direct reason)
硬件加速的 iframe 元素(比如 iframe 嵌入的页面中有合成层) demo video 元素 覆盖在 video 元素上的视频控制栏3D 或者 硬件加速的 2D Canvas 元素
demo:普通 2D Canvas 不会提升为合成层 demo:3D Canvas 提升为合成层硬件加速的插件,比如 flash 等等
在 DPI 较高的屏幕上,fix 定位的元素会自动地被提升到合成层中。但在 DPI 较低的设备上却并非如此,因为这个渲染层的提升会使得字体渲染方式由子像素变为灰阶(详细内容请参考: Text Rendering ) 有 3D transform backface-visibility 为 hidden对 opacity、transform、fliter、backdropfilter 应用了 animation 或者 transition(需要是 active 的 animation 或者 transition,当 animation 或者 transition 效果未开始或结束后,提升合成层也会失效)
demo:animationdemo:transition
will-change 设置为 opacity、transform、top、left、bottom、right(其中 top、left 等需要设置明确的定位属性,如 relative 等) demo
后代元素原因
有合成层后代同时本身有 transform、opactiy(小于 1)、mask、fliter、reflection 属性 demo 有合成层后代同时本身 overflow 不为 visible(如果本身是因为明确的定位因素产生的 SelfPaintingLayer,则需要 z-index 不为 auto) demo 有合成层后代同时本身 fixed 定位 demo 有 3D transfrom 的合成层后代同时本身有 preserves-3d 属性 demo 有 3D transfrom 的合成层后代同时本身有 perspective 属性 demooverlap 重叠原因
为什么会因为重叠原因而产生合成层呢?举个简单的栗子。
蓝色的矩形重叠在绿色矩形之上,同时它们的父元素是一个 GraphicsLayer。此时假设绿色矩形为一个 GraphicsLayer,如果 overlap 无法提升合成层的话,那么蓝色矩形不会提升为合成层,也就会和父元素公用一个 GraphicsLayer。
此时,渲染顺序就会发生错误,因此为保证渲染顺序,overlap 也成为了合成层产生的原因,也就是如下的正常情形。
当然 overlap 的原因也会细分为几类,接下来我们会详细看下。
重叠或者说部分重叠在一个合成层之上。
那如何算是重叠呢,最常见和容易理解的就是元素的 border box(content + padding + border) 和合成层的有重叠,比如: demo ,当然 margin area 的重叠是无效的( demo )。其他的还有一些不常见的情况,也算是同合成层重叠的条件,如下:
filter 效果同合成层重叠 demo transform 变换后同合成层重叠 demo overflow scroll 情况下同合成层重叠。即如果一个 overflow scroll(不管 overflow:auto 还是 overflow:scrill ,只要是能 scroll 即可) 的元素同一个合成层重叠,则其可视子元素也同该合成层重叠 demo假设重叠在一个合成层之上(assumedOverlap)。
这个原因听上去有点虚,什么叫假设重叠?其实也比较好理解,比如一个元素的 CSS 动画效果,动画运行期间,元素是有可能和其他元素有重叠的。针对于这种情况,于是就有了 assumedOverlap 的合成层产生原因,示例可见: demo 。在本 demo 中,动画元素视觉上并没有和其兄弟元素重叠,但因为 assumedOverlap 的原因,其兄弟元素依然提升为了合成层。
需要注意的是该原因下,有一个很特殊的情况:
如果合成层有内联的 transform 属性,会导致其兄弟渲染层 assume overlap,从而提升为合成层。比如: demo 。
层压缩
基本上常见的一些合成层的提升原因如上所说,你会发现,由于重叠的原因,可能随随便便就会产生出大量合成层来,而每个合成层都要消耗 CPU 和内存资源,岂不是严重影响页面性能。这一点浏览器也考虑到了,因此就有了层压缩(Layer Squashing)的处理。如果多个渲染层同一个合成层重叠时,这些渲染层会被压缩到一个 GraphicsLayer 中,以防止由于重叠原因导致可能出现的“层爆炸”。具体可以看如下 demo 。一开始,蓝色方块由于
translateZ 提升为了合成层,其他的方块元素因为重叠的原因,被压缩了一起,大小就是包含这 3 个方块的矩形大小。
当我们 hover 绿色方块时,会给其设置 translateZ 属性,导致绿色方块也被提升为合成层,则剩下的两个被压缩到了一起,大小就缩小为包含这 2 个方块的矩形大小。
当然,浏览器的自动的层压缩也不是万能的,有很多特定情况下,浏览器是无法进行层压缩的,如下所示,而这些情况也是我们应该尽量避免的。(注:以下情况都是基于重叠原因而言)
无法进行会打破渲染顺序的压缩(squashingWouldBreakPaintOrder)
示例如下: demo
#ancestor { -webkit-mask-image: -webkit-linear-gradient(rgba(0,0,0,1), rgba(0,0,0,0)); } #composited { width: 100%; height: 100%; transform: translateZ(0); } #container { position: relative; width: 400px; height: 60px; border: 1px solid black; } #overlap-child { position: absolute; left: 0; top: 0 ; bottom: 0px; width: 100%; height: 60px; background-color: orange; }
Text behind the orange box.
查看更多关于无线性能优化:Composite_html/css_WEB-ITnose的详细内容...
声明:本文来自网络,不代表【好得很程序员自学网】立场,转载请注明出处:http://www.haodehen.cn/did114765