一。层叠上下文
1. 首先我们要先明确一个概念:层叠上下文
层叠上下文:让HTML元素在2D平面堆叠出3D的视觉效果,根据层叠规则将哪个元素置于视觉最近处,哪个次之,以此类推。且每个层叠上下文对象都是一个渲染图层。
2. 如何形成层叠上下文:
- 文档根元素
- position不为初始值且z-index不为0或auto
- opacity属性值小于1
- flex布局元素
- will-change值设定为非初始值
- transform不为none
- filter不为none
- 还有更多到 fed.taobao.org/blog/taofed… 观看
3. 层叠顺序:
层叠等级的比较只有在当前层叠上下文元素中才有意义。不同层叠上下文中比较层叠等级是没有意义的。
同一层叠上下文且不考虑css3,层叠顺序如下:
二。复合图层,渲染图层
明确了层叠上下文的定义以后,我们来看下复合图层和渲染图层。
1. 两者定义与关系
定义:
- 渲染图层:是页面的普通的文档流,我们虽然可以使用绝对定位相对定位来脱离文档流,但是它仍属于默认复合层,都公用同一个绘画上下文对象
- 复合图层:它会单独分配系统资源,每个复合图层都有一个独立的GraphicsContext(当然也会脱离文档流,这样一来,不管复合图层中如何变化,都不会影响默认图层内的重绘重排),硬件加速就用在了这里
关系:
- 某些特殊的渲染层会被提升变为复合图层。复合图层拥有单独的 GraphicsLayer,而其他不是复合图层的渲染层,则跟随第一个拥有 GraphicsLayer 的父层。
- 每个GraphicsLayer 都有一个 GraphicsContext,GraphicsContext 会负责输出该层的位图,位图是存储在共享内存中,作为纹理上传到 GPU 中,最后由 GPU 将多个位图进行合成,然后显示到屏幕上。
简单来理解就是拥有层叠上下文属性的元素会生成一个新的层叠上下文,每个层叠上下文对象都是一个渲染图层,渲染图层和复合图层是两个不同的概念,渲染图层更像是一个纯2维的概念,无论怎么层叠都最终归依于根层叠上下文。而某些特殊的渲染层会被提成为复合图层,复合图层则完全脱离根层叠上下文,相当于开辟新的位面,且由GPU合成。
2. 如何创建复合图层?
- 3D转换:translate3d,translateZ依此类推;
- video,canvas,iframe元件
- transform和opacity经由Element.animate();
- transform和opacity经由СSS过渡和动画;
- will-change;
- 拥有加速 CSS 过滤器的元素filter;
- 元素有一个z-index较低且包含一个复合层的兄弟元素(换句话说就是该元素在复合层上面渲染)
- 元素有一个包含复合层的后代节点(换句话说,就是一个元素拥有一个子元素,该子元素在自己的层里)
- 更多参考 fed.taobao.org/blog/taofed…
3. 复合图层的作用?
- 复合层的位图,会交由GPU合成。比CPU处理的要快
- 需要repaint时只需要repaint本身,不会影响到其他层。
- 对于 transform 和 opacity 效果,不会触发 layout 、layer和 paint,直接进入合成线程处理
- CPU 和 GPU 之间的并行性,可以同时运行以创建高效的图形管道。
详细点,为什么可以提升?与本来的区别是什么?
重排的渲染流水线
重绘的渲染流水线。
合成
从上面的对比可以看出,我们使用了CSS的transform:translate来实现动画效果,这可以避开重排和重绘阶段,直接在非主线程上执行合成动画操作。这样的效率是最高的,因为是在非主线程上合成,并没有占用主线程的资源,另外也避开了布局和绘制两个子阶段,所以相对于重绘和重排,合成能大大提升绘制效率。其实这就是硬件加速
4. 怎么使用呢?使用的时候注意什么?
在写 Web 应用的时候,你可能经常需要对某个元素做几何形状变换、透明度变换或者一些缩放操作,如果使用 JavaScript 来写这些效果,会牵涉到整个渲染流水线,所以 JavaScript 的绘制效率会非常低下。这时你可以使用 will-change 来告诉渲染引擎你会对该元素做一些特效变换,CSS 代码如下:
.box {
will-change: transform, opacity;
}
这段代码就是提前告诉渲染引擎 box 元素将要做几何变换和透明度变换操作,这时候渲染引擎会将该元素单独实现一帧,等这些变换发生时,渲染引擎会通过合成线程直接去处理变换,这些变换并没有涉及到主线程,这样就大大提升了渲染的效率。这也是 CSS 动画比 JavaScript 动画高效的原因。所以,如果涉及到一些可以使用合成线程来处理 CSS 特效或者动画的情况,就尽量使用 will-change 来提前告诉渲染引擎,让它为该元素准备独立的层。但是凡事都有两面性,每当渲染引擎为一个元素准备一个独立层的时候,它占用的内存也会大大增加,因为从层树开始,后续每个阶段都会多一个层结构,这些都需要额外的内存,所以你需要恰当地使用 will-change.
其实看到这里我一直有个疑问:浏览器开发者工具里面看到图层layer究竟是渲染层还是复合图层?
我一直都误解了,其实渲染层不会在Layers里面看见,Layers里面看见的是复合图层。渲染层的分层只是在一个默认复合图层上分的,不会显示在Layers上,也不会放进GPU里面。