关于css:css-渲染优化

43次阅读

共计 6040 个字符,预计需要花费 16 分钟才能阅读完成。

在日常的开发中,通过应用 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

正文完
 0