当前位置: 代码迷 >> 综合 >> SVG/Canvas会触发重绘(repaint)和重排/回流(reflow)吗?
  详细解决方案

SVG/Canvas会触发重绘(repaint)和重排/回流(reflow)吗?

热度:63   发布时间:2023-11-14 11:20:35.0

?
我有一个同门认为SVG里元素的改变不会触发重排,但是我觉得会,所以这篇文章需要被扩展一下,想要深入讨论一下这个问题。

一、什么是重绘和重排?

1.1 浏览器渲染的过程

  1. 根据html文件构建DOM树和CSSOM树。构建DOM树期间,如果遇到JS,阻塞DOM树及CSSOM树的构建,优先加载JS文件,加载完毕,再继续构建DOM树及CSSOM树。
  2. 构建渲染树(render Tree)。
  3. 页面的重绘(repaint)与重排/回流(webkit引擎—Safari称作reflow,Blink引擎—Chrome称作layout)。页面渲染完成后,若JS操作了DOM节点,根据JS对DOM操作动作的大小,浏览器对页面进行重绘或是重排。

重绘:某些元素的外观被改变,例如:元素的填充颜色。
重排:重新生成布局,重新排列元素。当DOM的变化影响了元素的几何信息(元素的的位置和尺寸大小),浏览器需要重新计算元素的几何属性,将其安放在界面中的正确位置,这个过程叫做重排。

1.2 减少重排的传统方法

  1. 样式集中改变
  2. 批量修改DOM元素
  3. 离线DOM元素,display:none
  4. 使用 absolute 或 fixed 脱离文档流

虽然修改元素的几何属性会导致浏览器触发重排或重绘时。但现代浏览器优化了这个过程,它会把这些操作放进渲染队列,等到队列中的操作到了一定的数量或者到了一定的时间间隔时,浏览器就会批量执行(flush)这些操作。

但有些操作会引起浏览器提前flush队列,比如,当我们向浏览器请求以下style信息时,就会提前让浏览器flush队列:

  • offsetTop,offsetLeft,offsetWidth,offsetHeight
  • scrollTop/Left/Width/Height
  • clientTop/Left/Width/Height
  • width,height
  • 请求了getComputedStyle()或者IE的currentStyle

原因:
请求以上这些值时,浏览器需要清空队列,计算出最新的元素尺寸和位置样式信息(重绘回流),因为浏览器认为队列中的某些操作会造成我们获取的值并不是最精确的!

1.3 减少重排的现代方法

1.3.1 Document.Fragment

在使用JavaScript来操作DOM元素时,比如使用appendChild()方法。每次调用该方法时,浏览器都会重新渲染页面。如果大量的更新DOM节点,则会非常消耗性能,影响用户体验。
JavaScript提供了一个文档片段DocumentFragment的机制。如果将文档中的节点添加到文档片段中,就会从文档树中移除该节点。把所有要构造的节点都放在文档片段中执行,这样可以不影响文档树,也就不会造成页面渲染。当节点都构造完成后,再将文档片段对象添加到页面中,这时所有的节点都会一次性渲染出来,这样就能减少浏览器负担,提高页面渲染速度。(实际上还是一种批量修改DOM的方法)

1.3.2 requestAnimationFrame

网页的动画效果主要有两种实现方式:

  1. CSS3动画
    – CSS是关键帧动画,补间动画部分由浏览器完成,便于浏览器进行优化,可以更好控制动画执行过程
    – CSS的动画执行在合成线程,专事专干,不阻塞主线程,合成线程的动画也不会触发回流和重绘
    – CSS动画允许在GPU,专注渲染,更快(复合图层的概念,后面会提到)
  2. JS动画
    – JS是逐帧动画,每一帧都是由代码控制,操作不当,极易引发回流
    – JS的动画执行在主线程,主线程还有其他任务要执行,容易引发阻塞和等待,降低动画执行效率
    – JS动画运行在CPU,但CPU还有其他任务,易受影响

既然JS动画看上去这么拉,为什么要使用JS动画?因为可以通过编程实现复杂的动画效果。通常会使用setTimeout或者setInterval,以及requestAnimationFrame。

然而相比于setTimeout和setInterval而言,requestAnimationFrame有两个优点:

  1. requestAnimationFrame 会把每一帧中的所有DOM操作集中起来,在一次重绘或回流中就完成,并且重绘或回流的时间间隔紧紧跟随浏览器的刷新频率,一般来说,这个频率为每秒60帧。
  2. 在隐藏或不可见的元素中,requestAnimationFrame将不会进行重绘或回流,这当然就意味着更少的的CPU、GPU和内存使用量。

二、Canvas会触发重绘和重排吗?

除非Canvas本身的位置或者大小发生变化,影响了render tree,才会发生重排和回流。

理由是:重绘和回流都是相对于render tree上的元素而言的,而对canvas本身进行绘制并未对页面任何其他元素做更改,故只会引起Canvas画布本身的重绘。

credit to.

html canvas 改变,html5 –
每当有任何变化时,Canvas会重绘自己吗?
Canvas vs HTML DOM?
操作canvas 会不会导致页面reflow?

三、SVG会触发重绘和重排吗?

首先说结论:我现在觉得SVG其实并不会触发重排,因为SVG内存在一个坐标系,所有的SVG内的元素在SVG画布上是绝对定位的(除<g>内的子元素是相对于<g>定位的,aka相对定位)。仅仅更新SVG内部的元素,只会相对于SVG(root),对其所有子元素进行布局的计算,不能称作浏览器的重排。但SVG内的DOM元素还是还会在DOM树里,也会在Render树里,只不过针对文档流而言,不会有位置的改变,所以不会引起重排?
至于为什么当SVG内的元素多了之后,效率相比于Canvas会低很多:

  1. Immediate Mode(即时模式):Canvas没有DOM或文档对象模型。在使用Canvas绘制像素时,绘图指令执行了就不管了,减少了维护图形内部模型所需的额外内存。
  2. Retained Mode(保留模式):在使用SVG绘制图像的时候,绘制的每个对象都会添加到浏览器的内部模型中,使得性能降低。除此之外,SVG依旧依赖于Render树进行渲染,只不过不存在“重排”。

3.2 浏览器渲染时,产生图层

浏览器在渲染一个页面时,会将页面分为很多个图层,图层有大有小,每个图层上有一个或多个节点。在渲染DOM的时候,浏览器所做的工作实际上是:

  1. 获取DOM后分割为多个图层
  2. 对每个图层的节点计算样式结果 (recalculate style–样式重计算)
  3. 为每个节点生成图形和位置 (layout–重排,回流)
  4. 将每个节点绘制填充到图层位图中 (paint–重绘)
  5. 图层作为纹理上传至GPU
  6. 组合多个图层到页面上生成最终屏幕图像 (Composite Layers–图层重组)

产生图层的条件:

  1. 拥有具有3D变换的CSS属性:transform:rotate(7deg); rotateX()rotateY()rotateZ()
  2. 使用加速视频解码的节点
  3. <canvas>节点
  4. CSS3动画的节点
  5. 拥有CSS加速属性的元素(will-change)
    ?
    https://www.cnblogs.com/lichuntian/p/8616107.html

credit to
浅谈浏览器的图层与重绘重排(详细),以及如何用于性能优化
层叠上下文 渲染图层 复合图层(硬件加速)区别与联系

四、如何减少SVG所带来的重绘和重排?

在这里插入图片描述
针对SVG的性能优化

https://www.w3.org/TR/SVG11/struct.html#Head
在这里插入图片描述