使用溢出时减少滚动卡顿:滚动;

Posted

技术标签:

【中文标题】使用溢出时减少滚动卡顿:滚动;【英文标题】:Reducing scroll jank when using overflow-y: scroll; 【发布时间】:2017-01-08 17:50:33 【问题描述】:

我有一个 2 列布局,右侧列是一个可滚动的结果列表,最多包含 200 个项目结果(基本上只是一个带有 overflow-y: scroll; 集的 ul)

我发现,当您滚动时,右侧的列会导致一些卡顿(这在低端硬件上尤其明显)。

在 chrome 时间轴中,当我滚动列时,我可以看到一些主要的“更新层树”。 有什么办法可以弄清楚为什么“更新图层树”如此冗长以及哪些 CSS 属性对速度的影响最大? 当我点击它时 - 只告诉我它运行了多长时间而不是其他信息。

我在每个 li 中都有一些表现不佳的 CSS(例如过滤器、变换、框阴影等)- 如果我一个一个地删除每个 SASS 文件,它会提高滚动性能(并最终在我删除所有 CSS 后消除卡顿),但显然很难确定哪些 css 属性没有执行此操作。

我想知道我是否遗漏了 chrome 时间线中对这方面有帮助的内容?

我尝试使用 will-change CSS 属性将滚动提升到不同的层/强制硬件加速 - 但这并没有太大的区别。

我滚动时也没有执行任何 javascript 事件。

限制为少于 200 个项目不是一种选择。

我已经设置了一个示例(我知道它的项目列表较长,但这仅用于演示目的): https://github.com/jooj123/jank/blob/master/scroll.html

真正有趣的是,如果我删除 overflow-y: scroll;(在上例中的 .map-search__results 中),滚动变得非常平滑并且卡顿消失 - 为什么会有这么大的效果?

这是 chrome 时间线(基本上只有很长的“更新层树”部分):

修复:

Paul's answer about will-change: transform is spot on,特别是这条花絮帮助了:

“添加 will-change: transform;。一旦添加,“滚动重绘”矩形就消失了。”

但要小心,因为它并不总是适用于您的代码库,检查滚动性能问题指示器以确保“滚动重绘”已关闭,对我来说,我还必须禁用 -webkit-clip-path 属性(在我的在其中一个子 div 中设置的实际代码库 - 不在提供的演示中,请参阅:

【问题讨论】:

可能的领导:***.com/questions/25724126/… 如果您需要优化建议,我建议发布与这 200 个元素相关的代码 我看过那个帖子,并没有真正给我任何关于如何诊断问题的想法。我在滚动时没有使用 javascript 设置样式,并且在 chrome 时间轴中没有看到图层入侵 过去对我来说,将溢出放在包装 div 上比直接放在 ul 上效果更好 你看过这篇文章了吗? ***.com/questions/12969228/… @carol-mckay 感谢您的建议,但这并没有产生巨大的整体差异 【参考方案1】:

我试了your test page看了看。

首先,对于滚动性能问题,我喜欢在“渲染”窗格中打开一些复选框:

我们可以立即看到这个 div 被称为“repaints on scroll”。然后,您可以在实验性图层面板中验证:

(我在树中右键单击并选择“显示内部层”顺便说一句)

现在大的“更新层树”成本通常是由很多层或几层尺寸非常大的层引起的。在这种情况下,我们真的两者都没有。但是我们确实有一个滚动层,它没有得到复合滚动。

复合滚动 == 快速滚动 == 通常是默认值。但是在这种情况下,我们会退回到“主线程滚动”,这真的很慢而且很糟糕。从好的方面来说,crbug.com/381840 即将被修复,它应该会自动修复这个测试用例。耶!但我们现在可以解决它。

解决方法

您确实提到您尝试过will-change,但没有效果,但我自己尝试效果很好。它属于设置了overflow 的元素,在本例中是.map-search__results 选择器。添加will-change: transform;。一旦添加,“滚动重绘”矩形就消失了。然后用时间线测量,效果会好很多。

这是之前/之后的视图:

link to viewer

祝你好运!

【讨论】:

只是让您知道,在我的实际代码(不是提供的演示)中,will-change: transform 不起作用,这是由于在其中一个孩子中设置了-webkit-clip-path: inset(100%);(通过自动前缀插件) divs - 这似乎对will-change 有一些影响。一旦我删除了它并添加了will-change,滚动性能问题指示器就消失了,性能提升很大!! 噢,保罗。感谢诊所和 rawgit 参考;很有用! 我在滚动聊天记录时遇到了类似的情况。 will-change: transform 删除了 repaint on scroll 尽管我仍然有相当大的卡顿( 27fps )。需要注意的一件事是 Painting 现在比 Rendering 花费更多时间。此外,当我在聊天之间切换时,没有任何东西被绘制(但在 DOM 树中可见),如果我滚动并单击并拖动该区域,则会出现东西(如果添加了 will-change: tranform)。有什么想法吗? 但是我们应该怎么处理position: fixed孩子呢?任何应用了will-change: transform 的容器都将它们定位到与其内容相关的位置,而不是视口。 哇,这对我正在开发的应用程序有很大帮助,谢谢。【参考方案2】:

如果在滚动时您的问题明显可见,您应该能够在开发者工具中同时打开 timelinestyles 选项卡,取消选中适用的样式在样式选项卡中一一添加到您的元素,看看它如何影响滚动。这样您就可以独立测试每个样式规则,而不是测试整个 CSS 文件。如果您在 CSS 中使用 sourcemaps,您应该能够轻松地在实际的 CSS 文件中找到违规规则并进行调整。

如果您不熟悉使用样式选项卡:打开 Chrome 开发人员工具后,使用检查器工具(开发人员窗格的左上角)选择您怀疑可能会给您带来问题的项目。为选定元素关联或覆盖的所有样式都将在样式选项卡中列出。然后,您可以将它们一一关闭。您也可以使用检查器在嵌套项中钻取任意深度。

更新:

我在您编辑后拉下代码并仔细查看了它。我注意到您的溢出出现在您的 ul 项目上。就我个人而言,我从来没有在ul 上使用溢出(通常更喜欢div 之类的容器),所以出于好奇,我从你的ul 中删除了overflow-y:scroll,并将其放在ul 的包含div .right-content (并将其高度设置为 100%),然后滚动滚动消失了。

至于为什么,我只能推测,但我相信这与您滚动的项目数量有关。当您的溢出出现在 UL 上时,您将直接滚动所有这些子 li 项目。当它在包含div 时,实际上您只滚动ul,所以我相信这个过程在计算移动项目的位置时确定的数学要少得多。 1 div 定位调整 vs. 数百 li 调整。

如果您比较上图中的时间线,您会发现差异。最上面的是ul 上带有overlflow-y:scroll 的原始版本。您可以将鼠标悬停在更新树过程中的所有这些小线条上,并看到有数百个正在绘制的项目。这些是您的 li 项目。在第二个时间线中是我从ul 中删除溢出并将其应用于包含div 的版本。您可以在此时间线中看到更新树过程只有一项要绘制,即ul。如果您查看商品的尺寸,您也会看到尺寸上的差异。

【讨论】:

我已经更新了我的问题。我会给你一个赞成票,但我不认为这是解决问题的一种过快的方法(而且我知道检查员) - 它也没有回答为什么 overflow-y: scroll 这么慢 - 如果我删除所有卡顿消失 对时间线进行了进一步研究,发现如果您查看更新树过程,您可以看到所有正在绘制的项目。您可以将鼠标悬停在它们上方并查看差异。我在概述这一点的答案中添加了图像和描述。 这在帧数上产生了几毫秒的差异,但主要问题是“更新层树”

以上是关于使用溢出时减少滚动卡顿:滚动;的主要内容,如果未能解决你的问题,请参考以下文章

使用溢出时在移动/ios上滚动缓慢:滚动

内容溢出时无法滚动

使用溢出时无法隐藏滚动条:自动

内容溢出时的 Tailwind CSS 水平滚动

使用溢出时没有滚动条:自动和 div 高度

解决首页中可滚动区域出现卡顿的问题