传统分层策略
在 HTML5 和 CSS3 之前,已经有了分层的概念,但那时专指元素的层叠上下文。比如经常使用的 CSS Position 属性,可以使用 z-index 定义元素 Z 轴的位置。当然,Position 只是层叠上下文核心思想的冰山一角,具体层次的划分可以参考下图:
*到了 CSS3 的时代,除了 Position( 定位 )、Overflow( 溢出 )之外,Transform( 转换 )、Opacity( 透明度 )、Filter( 滤镜 )等等也可以创建层叠上下文。
绘图过程
层叠上下文主要是为了让网页内容有垂直方向的视觉效果。为了达到这样的效果,渲染引擎在绘制 HTML 元素的时候,会根据 CSS 的部分代码( 参考层叠上下文示意图 ),创建多个不同的绘制层( PaintLayer ),并为每一个绘制层调用绘图操作。
绘图过程按序分为以下三个阶段:
- 绘制背景和边框;
- 绘制浮动内容;
- 绘制内容部分。
*以上每个阶段都可能出现子元素,遇到子元素则优先遍历和绘制子元素。
每个绘制层保存的图像都不一样,但最终,渲染引擎会将所有绘制层合并起来( 即合成层 ),并储存在内存中,此时,我们的页面也就展现到了屏幕上。
分层绘制的优点
如果网页中有部分元素更新,则只是重新布局与绘制和该节点有关的一层或几层,并将它们和其他之前绘制完的层合并起来即可。不需要重新绘制整个页面,减少了重绘的开销。
分层策略的扩展
上文我们简单介绍了,在 HTML5 和 CSS3 之前,分层策略专指层叠上下文。但随着 HTML5 不断加入图形和多媒体功能,渲染引擎会为某些非常耗时的节点单独创建合成层。这些合成层与我们前面介绍的合成层,各自独立储存,互不影响( 包括重绘发生时 )。
常见合成层示意图:
*各合成层的前后关系可以参考示意图。各层的创建时间,也和图上的顺序一样,由基础层开始,最后创建 Canvas 层。Canvas 层在执行 JavaScript 代码时候才会开始创建。
GPU 渲染机制
渲染引擎对某些节点单独使用合成层,往往会伴随着硬件加速渲染机制( GPU 渲染机制(1) )的引入,因为很多 HTML5 的新功能,它必须使用硬件加速才能达到比较好的渲染效果,比如 Canvas 2D、Video 等;还有某些新功能,不使用硬件加速根本无法进行渲染,比如 CSS 3D 变形、WebGL 等。可以说,合成层与 GPU 渲染往往都是成对出现的。
(1) GPU,即计算机中专门用来进行绘图运算的处理器。
下面列举了常见的使用单独合成层和 GPU 渲染的情况:
1、使用了 CSS 3D 属性( transform )或者 CSS 透视效果( perspective )的节点;
例子:
<style>
#div1 {
width: 200px;
height: 100px;
background: #ff0000;
transform: rotateX(120deg);
}
</style>
<div id="div1"></div>
*打开最新版谷歌浏览器,找到 Layers ,可以查看分层效果,以下截图同此。
2、Video 和 Canvas 节点;
例子:
<canvas id="myCanvas" width="300" height="150"></canvas>
<script>
var c = document.getElementById("myCanvas");
var ctx = c.getContext("2d");
ctx.fillStyle = "#0000ff";
ctx.fillRect(20, 20, 150, 100);
</script>
3、使用了 CSS 透明效果( opacity )的动画或者 CSS 变换( transform )的动画的节点( 动画执行的过程中 )。
例子:
<style>
#div1 {
width: 100px;
height: 100px;
background: #009688;
animation: myAnimation 5s;
}
@keyframes myAnimation {
from {
opacity: 1;
transform: translate(0, 0);
}
to {
opacity: .1;
transform: translate(100px, 0);
}
}
</style>
<div id="div1"></div>
4、使用了 CSS 滤镜( Filters )、 CSS 倒影( Reflection )等属性的节点,并且它的子节点中包括一个合成层。
例子:
<style>
#div1 {
width: 200px;
height: 100px;
background: #009688;
filter: blur(5px);
}
#div2 {
width: 200px;
height: 100px;
background: #FFC107;
-webkit-box-reflect: below 5px;
}
/* 设置子节点为合成层 */
p {
transform: rotateX(120deg);
}
</style>
<div id="div1">
<p></p>
</div>
<div id="div2">
<p></p>
</div>
5、将 CSS 的 will-change 属性值设置为 opacity、transform 等或transform 属性值设置为 translateZ(0),可以将节点提升为合成层。
例子:
<style>
#div1 {
width: 200px;
height: 100px;
background: #009688;
will-change: opacity;
/* transform: translateZ(0); */
}
</style>
<div id="div1"></div>
6、有一个 Z-index 值比自己小的兄弟节点,且该兄弟节点是一个合成层。
例子:
<style>
#div1,
#div2 {
width: 100px;
height: 100px;
background: red;
position: absolute;
z-index: 10;
}
/* 设置兄弟节点为合成层 */
#div2 {
background: blue;
z-index: 1;
will-change: transform;
}
</style>
<div id="div1"></div>
<div id="div2"></div>
合成化绘图
CPU 渲染机制
在介绍合成化绘图之前,我们有必要引入另外一种常见的绘图机制,即软件渲染机制( CPU 渲染机制(1) )。
软件渲染使用 CPU 来绘制每一个绘制层的内容,并由 CPU 合成,合成层最终保存在 CPU 内存中。
软件渲染是浏览器最早使用的渲染方式之一,主要用于处理简单绘图,比如,画点、画线、画文字、画图形和图片等等。很多没有那些需要硬件加速内容的网站,例如论坛、博客等都采用这项技术来完成绘图。
(1)CPU,即计算机的中央处理器。
合成化绘图
目前比较流行的做法是,CPU 渲染和 GPU 渲染相结合的方式,也就是" 合成化绘图 "。
首先,合成绘制层的工作由 GPU 完成;
其次,对于每个合成层的每一个绘制层,可以有两种不同的选择:某些层( 主要是基本的 HTML 元素 )使用 CPU 来绘图,其他一些层( 主要是HTML5 的新功能 )使用 GPU 来绘图。对于使用 CPU 绘图的绘制层,图像会保存在 CPU 内存中,之后传输到 GPU 内存中( CPU 与 GPU 在不同的线程中工作 ),用以完成最后的合成工作。
如有错误,欢迎指正,本人不胜感激。