浏览器渲染图层VS复合图层(硬件加速)


我们知道拥有层叠上下文属性的元素被提升为单独的一层,这些层被称为渲染图层,它与复合图层是不同的概念。层叠上下文为了能够在一个2D平面堆叠出3D的效果。复合图层则是相当于独立出许多层,每个层里面都有一个层叠上下文对象( stacking context)。

浏览器渲染的图层一般包含两大类:普通图层以及复合图层

  • 普通图层,又称默认复合层,是页面普通的文档流。我们虽然可以通过绝对定位,相对定位,浮动定位脱离文档流,但它仍然属于默认复合层,共用同一个绘图上下文对象(GraphicsContext)。
  • 复合图层,它会单独分配资源(当然也会脱离普通文档流,这样一来,不管这个复合图层中怎么变化,也不会影响默认复合层里的回流重绘)

复合成层

某些特殊的渲染层会被提升为复合成层(Compositing Layers),复合图层拥有单独的 GraphicsLayer,而其他不是复合图层的渲染层,则和其第一个拥有 GraphicsLayer 父层共用一个。

每个 GraphicsLayer 都有一个 GraphicsContext,GraphicsContext 会负责输出该层的位图,位图是存储在共享内存中,作为纹理上传到 GPU 中,最后由 GPU 将多个位图进行合成,然后 draw 到屏幕上,此时,我们的页面也就展现到了屏幕上。

复合图层的作用?(为什么硬件加速会使页面流畅)

一般一个元素开启硬件加速后会变成复合图层,可以独立于普通文档流中,改动后可以避免整个页面重绘,提升性能。

复合图层的使用注意事项?
但是尽量不要大量使用复合图层,否则由于资源消耗过度,页面反而会变的更卡

使用3D硬件加速提升动画性能时,最好给元素增加一个z-index属性,人为干扰复合层的排序,可以有效减少chrome创建不必要的复合层,提升渲染性能,移动端优化效果尤为明显。

复合图层创建标准

  • 3D转换:translate3d,translateZ依此类推;
  • 元件;
  • transform和opacity经由Element.animate();
  • transform和opacity经由СSS过渡和动画;
  • 有合成层后代同时本身 fixed 定位
  • will-change;
  • filter;
    等等具体可看 performance-composite

为什么transform没有触发repaint呢?

简而言之,transform动画由GPU控制,支持硬件加载,并不需要软件方面的渲染。

查看复合图层

Chrome源码调试 -> More Tools -> Layers

1
2
3
4
5
6
7
8
9
<!doctype html>
<html>
<body>
  <div style="transform: rotateY(30deg) rotateX(-30deg); width: 200px;">
    I am a strange root.
  </div>
  <div>where are you</div>
</body>
</html>

在这里插入图片描述
Chrome源码调试 -> More Tools -> Rendering -> Layer borders
橘黄色框部分就是硬件加速层
在这里插入图片描述
证明硬件加速后CSS动画不会引起重绘?

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
<!doctype html>
<html>
<head>
  <style>
  div {
    animation-duration: 5s;
    animation-name: slide;
    animation-iteration-count: infinite;
    animation-direction: alternate;
    width: 200px;
    height: 200px;
    margin: 100px;
    background-color: gray;
  }
  @keyframes slide {
    from {
      transform: rotate(0deg);
    }
    to {
      transform: rotate(120deg);
    }
  }
  </style>
</head>
<body>
  <div>I am a strange root.</div>
</body>
</html>

在这里插入图片描述
可以观察到动画在动,而绘制指令并没有发生改变。也可以调节绘制时间戳看绘制指令变动。

举个反例:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
<!doctype html>
<html>
<head>
  <style>
  div {
    animation-duration: 5s;
    animation-name: slide;
    animation-iteration-count: infinite;
    animation-direction: alternate;
    width: 200px;
    height: 200px;
    margin: 100px;
    background-color: gray;
  }
  @keyframes slide {
    from {
      transform: rotate(0deg);
    }
    to {
      transform: rotate(120deg);
    }
  }
  </style>
</head>
<body>
  <div id="foo">I am a strange root.</div>
  <input id="paint" type="button" value="repaint">
  <script>
    var w = 200;
    document.getElementById('paint').onclick = function() {
      document.getElementById('foo').style.width = (w++) + 'px';
    }
  </script>
</body>
</html>

在这里插入图片描述
Chrome源码调试 -> More Tools -> Layers
一开始绘制指令没变,点击按钮会为div宽度加1px,关闭profiler后重新打开,可看到绘制指令增加了
在这里插入图片描述
参考文档:
gpu-animation-doing-it-right
performance-composite
Accelerated Rendering in Chrome