滚动与页面重绘

421 阅读2分钟

问题说明

一次偶然使用paint flashing中发现,项目中页面进行滚动操作时所有的元素竟然都在repaint

1.gif

0.0 于是决定稍微研究一下

排查过程

首先看了看实现代码,确实没有什么骚操作,就是普通的一个scroll: auto。随手看了看滚动时的火焰图

image.png 在滚动的过程中确实一直在paint操作(里面绿色的都是),但是并无fps卡顿。 虽然这种不影响性能,但还是想看看能否优化一下。

浏览器主要执行步骤

  • JavaScript:包含与视觉变化效果相关的js操作。包括并不限于:dom更新、元素样式动态改变、jQuery的animate函数等。

  • Style:样式计算。这个过程,浏览器根据css选择器计算哪些元素应该应用哪些规则,然后将样式规则落实到每个元素上去,确定每个元素具体的样式。

  • Layout:布局。在知道对一个元素应用哪些规则之后,浏览器即可开始计算它要占据的空间大小及其在屏幕的位置。

  • Painting:绘制。绘制是填充像素的过程。它涉及绘出文本、颜色、图像、边框和阴影,基本上包括元素的每个可视部分。绘制一般是在多个表面(通常称为层)上完成的。(paint和draw的区别:paint是把内容填充到页面,而draw是把页面反映到屏幕上)

  • Composite:合成。由于页面的各部分可能被绘制到多层,由此它们需要按正确顺序绘制到屏幕上,以便正确渲染页面。对于与另一元素重叠的元素来说,这点特别重要,因为一个错误可能使一个元素错误地出现在另一个元素的上层。

从上面来看,只需要让滑动元素composite成一个层级,那应该就可以阻止重绘

对于composite,还发现了一篇非常nice的文章:无线性能优化:Composite

will-change

首先想到的自然是will-change,说实话,MDN上对will-change的描述感觉有些模糊,先来尝试一下 加上will-change后的效果

2.gif 看起来确实没问题了。 但是看看will-change的描述:

image.png 既然也没出现丢帧的情况,那是没必要使用will-change,但是以后想要极限优化可以使用

    el.addEventLister('mouseenter', () => { el.style.willChange = 'scroll-position' });
    el.addEventLister('mouseleave', () => { el.style.willChange = 'unset' });

存在问题

有些web的scroll应该没有使用will-change也没存在上述重绘情况,之后遇到还有待跟进