在日常的开发中,通过应用css属性,做一些动效、动画时,会发现在页面有卡顿;
在Android低端机尤为显著,故须要晓得浏览器渲染以及优化伎俩
浏览器渲染流程
- 构建 DOM 树:浏览器将 HTML 解析成树形构造的 DOM 树,一般来说,这个过程产生在页面首次加载,或页面 JavaScript 批改了节点构造的时候。
- 构建渲染树:浏览器将 CSS 解析成树形构造的 CSSOM 树,再和 DOM 树合并成渲染树。
- 布局(Layout):浏览器依据渲染树所体现的节点、各个节点的CSS定义以及它们的从属关系,计算出每个节点在屏幕中的地位。Web 页面中元素的布局是绝对的,在页面元素地位、大小发生变化,往往会导致其余节点联动,须要从新计算布局,这时候的布局过程个别被称为回流/重排(Reflow)。
- 绘制(Paint):遍历渲染树,调用渲染器的 paint() 办法在屏幕上绘制出节点内容,实质上是一个像素填充的过程。这个过程也呈现于回流或一些不影响布局的 CSS 批改引起的屏幕部分重画,这时候它被称为重绘(Repaint)。实际上,绘制过程是在多个层上实现的,这些层咱们称为渲染层(RenderLayer)。
- 渲染层合成(Composite):多个绘制后的渲染层依照失当的重叠程序进行合并,而后生成位图,最终通过显卡展现到屏幕上。
以上是浏览器渲染的根本步骤,简述:
DOM tree + CSS tree == Render tree ==> Layout tree ==> PaintLayer => Composite PaintLayer => Composite
只有页面上元素有扭转,都会反复渲染以上局部步骤,在一帧内实现(16.77ms);
通过设置css 款式 ,做动画,在一帧内无奈渲染完,甚至来不及渲染,就会卡顿;
其中局部渲染卡顿,跟渲染层合成(Composite)有关系。
Composite
什么是渲染层合成
在 DOM 树中每个节点都会对应一个渲染对象(RenderObject),当它们的渲染对象处于雷同的坐标空间(z 轴空间)时,就会造成一个 RenderLayers,也就是渲染层。渲染层将保障页面元素以正确的程序重叠,这时候就会呈现层合成(composite),从而正确处理通明元素和重叠元素的显示,这步叫做“层叠上下文”
这个模型相似于 Photoshop 的图层模型,在 Photoshop 中,每个设计元素都是一个独立的图层,多个图层以失当的程序在 z 轴空间上叠加,最终形成一个残缺的设计图。
对于有地位重叠的元素的页面,这个过程尤其重要,因为一旦图层的合并程序出错,将会导致元素显示异样。
渲染对象(RenderObject)
一个 DOM 节点对应了一个渲染对象,渲染对象仍然维持着 DOM 树的树形构造。
渲染层(RenderLayer)
这是浏览器渲染期间构建的第一个层模型,处于雷同坐标空间(z轴空间)的渲染对象,都将归并到同一个渲染层中,因而依据层叠上下文,不同坐标空间的的渲染对象将造成多个渲染层,以体现它们的层叠关系。
因而满足造成层叠上下文条件的 RenderObject 肯定会为其创立新的渲染层,当然还有其余的一些非凡状况,为一些非凡的 RenderObject 创立一个新的渲染层,比方 overflow != visible 的元素。依据创立 RenderLayer 的起因不同,能够将其分为常见的 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 不为 visible
- NoPaintLayer
- 不须要 paint 的 PaintLayer,比方一个没有视觉属性(背景、色彩、暗影等)的空 div。
满足以上条件的 RenderObject 会领有独立的渲染层,而其余的 RenderObject 则和其第一个领有渲染层的父元素共用一个。
图形层(GraphicsLayer)
渲染层,和其第一个领有 GraphicsLayer 的父层共用一个。
每个 GraphicsLayer 都有一个 GraphicsContext,GraphicsContext 会负责输入该层的位图,位图是存储在共享内存中,作为纹理上传到 GPU 中,最初由 GPU 将多个位图进行合成,而后 draw 到屏幕上,此时,咱们的页面也就展示到了屏幕上。
合成层(CompositingLayer)
满足某些非凡条件的渲染层,会被浏览器主动晋升为合成层。合成层领有独自的 GraphicsLayer,
个渲染层满足哪些非凡条件时,能力被晋升为合成层呢?这里列举了一些常见的状况:
- 3D transforms:translate3d、translateZ 等
- video、canvas、iframe 等元素
- 通过 Element.animate() 实现的 opacity 动画转换
- 通过 SS 动画实现的 opacity 动画转换
- position: fixed
- 具备 will-change 属性
- 对 opacity、transform、fliter、backdropfilter 利用了 animation 或者 transition
常见实用**transforms:translate3d、translateZ **
来造成合成层,这样是对原先的布局没有影响
示例
其余合成状况
隐式合成
除此之外,在浏览器的 Composite 阶段,还存在一种隐式合成,局部渲染层在一些特定场景下,会被默认晋升为合成层。其实就是元素div之间互相重叠
上图所示:把position-1
生成合成层,前面的元素会互相重叠,为了层叠上下文展现正确;导致前面的元素间接隐式合成了;
合层压缩
如上图,目前没有遇到
合成爆炸
通过上图钻研,很容易就产生一些不在预期范畴内的合成层,当这些不合乎预期的合成层达到一定量级时,就会变成层爆炸。
须要排查是否有重叠,是否通过z-index
来调节层叠程序
优化
长处
- 合成层的位图,会交由 GPU 合成,比 CPU 解决要快得多;
- 当须要 repaint 时,只须要 repaint 自身,不会影响到其余的层;
- 元素晋升为合成层后,transform 和 opacity 才不会触发 repaint,如果不是合成层,则其仍然会触发 repaint。(只有css动画,才能够,js去管制还是会重排、重绘的)应用 transform 或者 opacity 来实现动画成果
更新元素几何属性(产生重排)
浏览器会触发从新布局,解析之后的一系列子阶段,这个过程就叫重排。无疑,重排须要更新残缺的渲染流水线,所以开销也是最大的
引发重排的状况
- 页面首次渲染;
- 浏览器窗口大小发生变化;
- 元素的内容发生变化;
- 元素的尺寸或者地位发生变化;
- 元素的字体大小发生变化;
- 激活CSS伪类;
查问某些属性或者调用某些办法;
- offsetTop、offsetLeft、offsetWidth、offsetHeight
- scrollTop、scrollLeft、scrollWidth、scrollHeight
- clientTop、clientLeft、clientWidth、clientHeight
- width、height
- getComputedStyle()
- getBoundingClientRect()
- 增加或者删除可见的DOM元素。
更新元素绘制属性(产生重绘)
如果批改了元素的背景色彩,那么布局阶段将不会被执行,因为并没有引起几何地位的变换,所以就间接进入了绘制阶段,而后执行之后的一系列子阶段,这个过程就叫重绘。相较于重排操作,重绘省去了布局和分层阶段,所以执行效率会比重排操作要高一些。
引发重绘的状况
- 重排
- visibility: visible <=> hidden
- 色彩扭转
- 其余几何变动...
间接合成阶段
咱们应用了CSS的transform来实现动画成果,这能够避开重排和重绘阶段,间接在非主线程上执行合成动画操作。这样的效率是最高的,因为是在非主线程上合成,并没有占用主线程的资源,另外也避开了布局和绘制两个子阶段,所以绝对于重绘和重排,合成能大大晋升绘制效率。
毛病
- 绘制的图层必须传输到 GPU,这些层的数量和大小达到一定量级后,可能会导致传输十分慢,进而导致一些低端和中端设施上呈现闪动;
隐式合成容易产生适量的合成层,每个合成层都占用额定的内存,而内存是挪动设施上的贵重资源,过多应用内存可能会导致浏览器解体,让性能优化适得其s
其余优化
css
- 应用 transform 代替 top
- 应用 visibility 替换 display: none ,因为前者只会引起重绘,后者会引发回流(扭转了布局
- 防止应用table布局,可能很小的一个小改变会造成整个 table 的从新布局。
- 尽可能在DOM树的最末端扭转class,回流是不可避免的,但能够缩小其影响。尽可能在DOM树的最末端扭转class,能够限度了回流的范畴,使其影响尽可能少的节点。
- 防止设置多层内联款式,CSS 选择符从右往左匹配查找,防止节点层级过多。
- 将动画成果利用到position属性为absolute或fixed的元素上,防止影响其余元素的布局,这样只是一个重绘,而不是回流,同时,管制动画速度能够抉择 requestAnimationFrame,详见探讨 requestAnimationFrame。
- 防止应用CSS表达式,可能会引发回流。
将频繁重绘或者回流的节点设置为图层,图层可能阻止该节点的渲染行为影响别的节点,例如will-change、video、iframe等标签,浏览器会主动将该节点变为图层。
js:
- 防止频繁操作款式,最好一次性重写style属性,或者将款式列表定义为class并一次性更改class属性。
- 防止频繁操作DOM,创立一个documentFragment,在它下面利用所有DOM操作,最初再把它增加到文档中。
- 防止频繁读取会引发回流/重绘的属性,如果的确须要屡次应用,就用一个变量缓存起来。
对具备简单动画的元素应用相对定位,使它脱离文档流,否则会引起父元素及后续元素频繁回流。
总结
在做动画时尽可能的应用应用csstransforms属性做动画,,它跳过Layout和Paint阶段,间接进入渲染,所以会晋升局部性能,同时看看所属空间地位,是否晋升了合成层;还有留神元素是否重叠,避免合成层爆炸;这样至多能保障css 方面,尽可能性能没有问题。
剩下的请查看其余优化伎俩
示例代码
重叠
<style> .ball-running,.ball-running1 { width: 100px; height: 100px; animation: run-around 4s linear alternate 100; background: red; position: absolute; border-radius: 50%; } @keyframes run-around { 0% { top: 0; left: 0; } 25% { top: 0; left: 200px; } 50% { top: 200px; left: 200px; } 75% { top: 200px; left: 0; } } .position-1,.position-2,.position-3,.position-4 { position: absolute; width: 100px; height: 100px; background-color: red; border: #1b1b1b 1px solid; } .position-1 { transform: translateZ(10px); } .position-2 { top: 90px; left: 90px; // transform: translateZ(10px); } .position-3 { top: 180px; left: 180px; } .position-4 { top: 270px; left: 270px; } .parent-1 { //width: 100px; //height: 200px; background: #5b3a13; //overflow: auto; //transform: translateZ(10px); } .position-5 { }<style/> <div class="parent-1"> <div class="position-1"> 1 </div> <div class="position-2"> 2 </div> <div class="position-3"> 3 </div> <div class="position-4"> 4 </div> </div>
动画
<style>.ball-running,.ball-running1 { width: 100px; height: 100px; // animation: run-around2 4s linear alternate 100; background: red; position: absolute; border-radius: 50%; } @keyframes run-around { 0% { top: 0; left: 0; } 25% { top: 0; left: 200px; } 50% { top: 200px; left: 200px; } 75% { top: 200px; left: 0; } } //.ball-running { // width: 100px; // height: 100px; // animation: run-around 2s linear alternate 100; // background: red; // border-radius: 50%; //} //@keyframes run-around { // 0% { // transform: translate(0, 0); // } // 25% { // transform: translate(200px, 0); // } // 50% { // transform: translate(200px, 200px); // } // 75% { // transform: translate(0, 200px); // } //}</style><div class="ball-running"></div>
参考:
transform和left扭转地位的性能区别
GPU Accelerated Compositing in Chrome
浏览器层合成与页面渲染优化
浏览器渲染页面过程与页面优化
无线性能优化:Composite
浏览器渲染过程
渲染流程
Stick to Compositor-Only Properties and Manage Layer Count