我们知道拥有层叠上下文属性的元素被提升为单独的一层,这些层被称为渲染图层,它与复合图层是不同的概念。层叠上下文为了能够在一个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