关于transform加速

问:当我们提供position​定位通过改变 left、top​ 写一个小球摆动的动画,在执行之前写死10000次的for循环,动画会立即开始吗?

答:不会,js会阻塞住,因为js的同步执行顺序导致,按照书写顺序同步执行。

问:当我们使用 transform​来替换上述方案后,动画会立即开始吗?

答:会,这个设计到浏览器渲染原理,浏览器在遇到transform​时,进行单独分层处理,编译成绘制指令交给gpu​执行单独绘制,也就是我们常说的gpu加速

浏览器渲染原理

当我们输入url回车,浏览器发生了以下操作:

  1. dns解析,定向到服务器,返回页面;

  2. 解析html,同步解析

    1. 遇到普通的script​标签,会阻塞dom解析,等待script​解析加载完成才会继续
    2. 遇见async​的script​标签会进行立马加载并阻塞dom解析,加载完成立即执行,同时不保证多个async​的script​标签的执行顺序
    3. 遇见defer​的script​标签会进行立马下载,但要等到浏览器解析完dom后,空闲时间时执行,同时保证多个defer​的script​标签的执行顺序
    4. 遇到link​标签,不会阻塞解析
    5. 遇见prefetch​的link​标签不会进行立马加载,等到浏览器解析完dom及资源加载后,空闲时间时下载并加入缓存处理
    6. 遇见preload​的link​标签会进行立马加载,浏览器希望尽快获取到请求资源,同时不阻塞onload​,需要注明as​以标识文件类型,从而保证准确加载
    7. 生成domtree
  3. 解析css资源,生成cssom树

  4. 进行样式计算,主线程会遍历得到的 DOM 树,依次为树中的每个节点计算出它最终的样式,称之为 Computed Style。

    1. 在这一过程中,很多预设值会变成绝对值,比如red​会变成rgb(255,0,0)​;相对单位会变成绝对单位,比如em​会变成px
  5. 将domtree和cssom树进行结合生成布局树

    1. 布局阶段会依次遍历 DOM 树的每一个节点,计算每个节点的几何信息。例如节点的宽高、相对包含块的位置。

    2. 大部分时候,DOM 树和布局树并非一一对应。

      比如display:none​的节点没有几何信息,因此不会生成到布局树;又比如使用了伪元素选择器,虽然 DOM 树中不存在这些伪元素节点,但它们拥有几何信息,所以会生成到布局树中。还有匿名行盒、匿名块盒等等都会导致 DOM 树和布局树无法一一对应。

  6. 分层处理

    1. 主线程使用复杂的策略对布局进行分层,好处在于,每个单独的分层独立进行后续步骤,从而提升效率。

    2. 影响分层的一些属性:滚动条、堆叠上下文、transform、opacity 等样式都会或多或少的影响分层结果,也可以通过will-change​属性更大程度的影响分层结果。

    3. 指定单独分层:will-change: transform;

  7. 绘制

    1. 主线程为每个分层单独生成绘制指令集,用于描述如何绘制
    2. 绘制完成后,主线程将各个图层的绘制信息提交给合成线程
    3. 合成线程从线程池中开启多个线程对每个图层进行分块,划分更小区域
  8. 光栅化

    1. 合成线程将分好的块信息交给GPU完成光栅化

    2. 将每个分块进行位图转化,并优先处理靠近视口的分块,生成一个个「指引(quad)」信息

    3. 指引会标识出每个位图应该画到屏幕的哪个位置,以及会考虑到旋转、缩放等变形。

      变形发生在合成线程,与渲染主线程无关,这就是transform​效率高的本质原因。

    1. 合成线程会把 quad 提交给 GPU 进程,由 GPU 进程产生系统调用,提交给 GPU 硬件,完成最终的屏幕成像。

为什么 transform 的效率高?

因为 transform 既不会影响布局也不会影响绘制指令,它影响的只是渲染流程的最后一个「draw」阶段

由于 draw 阶段在合成线程中,所以 transform 的变化几乎不会影响渲染主线程。反之,渲染主线程无论如何忙碌,也不会影响 transform 的变化。

reflow 重排

重新计算layout树

当我们修改dom及影响布局的css时,需要进行重新解析计算,从而进行布局树的重新计算。

为了避免频繁layout计算,浏览器会合并操作,当js全部执行完才会进行统一计算,所以改动属性造成的重排是异步完成的。

这也导致了当js获取布局属性(getComputedStyle)时,可能无法获取到最新的布局信息(元素最新的尺寸信息),为了解决这个问题,浏览器会立马进行reflow​。

repaint 重绘

repaint 的本质就是重新根据分层信息计算了绘制指令。

当改动了可见样式(颜色、阴影等不影响尺寸的样式)后,就需要重新计算绘制指令,会引发 repaint。

由于元素的布局信息也属于可见样式,所以 reflow 一定会引起 repaint。