关于transform加速
关于transform加速
问:当我们提供position
定位通过改变 left、top
写一个小球摆动的动画,在执行之前写死10000次的for循环,动画会立即开始吗?
答:不会,js会阻塞住,因为js的同步执行顺序导致,按照书写顺序同步执行。
问:当我们使用 transform
来替换上述方案后,动画会立即开始吗?
答:会,这个设计到浏览器渲染原理,浏览器在遇到transform
时,进行单独分层处理,编译成绘制指令交给gpu
执行单独绘制,也就是我们常说的gpu加速
浏览器渲染原理
当我们输入url回车,浏览器发生了以下操作:
-
dns解析,定向到服务器,返回页面;
-
解析html,同步解析
- 遇到普通的
script
标签,会阻塞dom解析,等待script
解析加载完成才会继续 - 遇见
async
的script
标签会进行立马加载并阻塞dom解析,加载完成立即执行,同时不保证多个async
的script
标签的执行顺序 - 遇见
defer
的script
标签会进行立马下载,但要等到浏览器解析完dom后,空闲时间时执行,同时保证多个defer
的script
标签的执行顺序 - 遇到
link
标签,不会阻塞解析 - 遇见
prefetch
的link
标签不会进行立马加载,等到浏览器解析完dom及资源加载后,空闲时间时下载并加入缓存处理 - 遇见
preload
的link
标签会进行立马加载,浏览器希望尽快获取到请求资源,同时不阻塞onload
,需要注明as
以标识文件类型,从而保证准确加载 - 生成domtree
- 遇到普通的
-
解析css资源,生成cssom树
-
进行样式计算,主线程会遍历得到的 DOM 树,依次为树中的每个节点计算出它最终的样式,称之为 Computed Style。
- 在这一过程中,很多预设值会变成绝对值,比如
red
会变成rgb(255,0,0)
;相对单位会变成绝对单位,比如em
会变成px
- 在这一过程中,很多预设值会变成绝对值,比如
-
将domtree和cssom树进行结合生成布局树
-
布局阶段会依次遍历 DOM 树的每一个节点,计算每个节点的几何信息。例如节点的宽高、相对包含块的位置。
-
大部分时候,DOM 树和布局树并非一一对应。
比如
display:none
的节点没有几何信息,因此不会生成到布局树;又比如使用了伪元素选择器,虽然 DOM 树中不存在这些伪元素节点,但它们拥有几何信息,所以会生成到布局树中。还有匿名行盒、匿名块盒等等都会导致 DOM 树和布局树无法一一对应。 -
-
-
分层处理
-
主线程使用复杂的策略对布局进行分层,好处在于,每个单独的分层独立进行后续步骤,从而提升效率。
-
影响分层的一些属性:滚动条、堆叠上下文、transform、opacity 等样式都会或多或少的影响分层结果,也可以通过
will-change
属性更大程度的影响分层结果。 -
-
指定单独分层:will-change: transform;
-
-
绘制
- 主线程为每个分层单独生成绘制指令集,用于描述如何绘制
- 绘制完成后,主线程将各个图层的绘制信息提交给合成线程
- 合成线程从线程池中开启多个线程对每个图层进行分块,划分更小区域
-
光栅化
-
合成线程将分好的块信息交给GPU完成光栅化
-
将每个分块进行位图转化,并优先处理靠近视口的分块,生成一个个「指引(quad)」信息
-
指引会标识出每个位图应该画到屏幕的哪个位置,以及会考虑到旋转、缩放等变形。
变形发生在合成线程,与渲染主线程无关,这就是
transform
效率高的本质原因。
-
-
画
- 合成线程会把 quad 提交给 GPU 进程,由 GPU 进程产生系统调用,提交给 GPU 硬件,完成最终的屏幕成像。
为什么 transform 的效率高?
因为 transform 既不会影响布局也不会影响绘制指令,它影响的只是渲染流程的最后一个「draw」阶段
由于 draw 阶段在合成线程中,所以 transform 的变化几乎不会影响渲染主线程。反之,渲染主线程无论如何忙碌,也不会影响 transform 的变化。
reflow 重排
重新计算layout树
当我们修改dom及影响布局的css时,需要进行重新解析计算,从而进行布局树的重新计算。
为了避免频繁layout计算,浏览器会合并操作,当js全部执行完才会进行统一计算,所以改动属性造成的重排是异步完成的。
这也导致了当js获取布局属性(getComputedStyle)时,可能无法获取到最新的布局信息(元素最新的尺寸信息),为了解决这个问题,浏览器会立马进行reflow
。
repaint 重绘
repaint 的本质就是重新根据分层信息计算了绘制指令。
当改动了可见样式(颜色、阴影等不影响尺寸的样式)后,就需要重新计算绘制指令,会引发 repaint。
由于元素的布局信息也属于可见样式,所以 reflow 一定会引起 repaint。