避免不必要的绘制 - GIF 动画版本

Paul Lewis

避免渲染对于实现丝滑流畅的帧速率至关重要,尤其是在移动设备上。但有时,颜料会在最不寻常的地方出现。本文介绍了为什么动画 GIF 会导致出现不必要的绘制,以及您可以采用的超简单修正方法。

多层可爱

如您所知,现代浏览器可能会将多组 DOM 元素绘制成单独的“图像”,称为层。有时,整个网页只有一个图层,有时有数百个甚至在极少数情况下,例如数千个!

当 DOM 元素组合到一个层中并且其中一个元素在视觉上发生变化时,我们最终不仅需要绘制已更改的元素,还要绘制与更改后的元素重叠的层中的所有其他元素。将一个对象绘制在另一个对象上会导致被覆盖的像素永久“丢失”;如果您想重新绘制原始像素,则需要对它们进行重新绘制。

因此,我们有时需要将某个元素与其他元素分隔开来,这样在绘制时,我们就不需要重新绘制未更改的其他元素。例如,当您将固定页眉与可滚动内容结合使用时,您必须在内容每次滚动时重新绘制页眉以及新显示的内容。通过将标题放在单独的层中,浏览器可以优化滚动效果。滚动时,浏览器可以四处移动层(可能借助 GPU),并避免重新绘制任一层。

每增加一个层都会增加内存消耗并增加性能开销,因此目标是在保持良好性能的同时尽可能将页面分组。

这与动画 GIF 有什么关系?

我们来看一下这张图片:

一个分为四层的 Web 应用。
图 1:一个分为四个层的 Web 应用。

这是针对简单应用的潜在层设置。此处有四个层:其中三个(第 2 层到第 4 层)是界面元素;后层是加载器,恰好是动画 GIF。在正常流程中,您将在应用加载时显示加载器(第 1 层),然后在一切完成后显示其他层。但关键在于,您需要隐藏动画 GIF

可我为什么要把它隐藏起来呢?!

问得好。在理想情况下,浏览器只会为您检查 GIF 的可见性,并避免自动绘制。遗憾的是,检查动画 GIF 是否被遮挡或在屏幕上可见通常比简单绘制动画的开销更高,因此需要绘制动画。

理想情况下,GIF 位于自己的层中,浏览器只需对其进行绘制并将其上传到 GPU。但在最糟糕的情况下,您的所有元素都可能会被划分到一个图层中,并且浏览器必须重新绘制每个元素。完成后,它仍需要将所有内容上传到 GPU。尽管实际上用户甚至看不到 GIF,但所有这些操作对于每个 GIF 帧来说都是如此!

在桌面设备上,您也许可以避开这种绘制行为,因为 CPU 和 GPU 更强大,并且有充足的带宽在这两者之间传输数据。然而,在移动设备上,绘画成本极高,因此您必须小心谨慎。

哪些浏览器会受到此问题的影响?

不同浏览器的行为通常不同。目前,即使 GIF 被遮挡,Chrome、Safari 和 Opera 也会重新绘制。另一方面,Firefox 则会发现 GIF 被遮挡,不需要重新着色。Internet Explorer 仍然是一个黑盒子,即使是在 IE11 中,由于 F12 工具仍在开发中,因此无法确定是否正在进行任何重绘。

如何判断我是否遇到此问题?

最简单的方法是使用 Chrome 开发者工具中的“Show paint rectangles”。加载开发者工具并按下右下角的齿轮图标 (齿轮图标),然后选择 Rendering 部分下的 Show paint rectangles

在 Chrome DevTools 中启用“Show paint rectangles”
图 2:在 Chrome 开发者工具中启用“Show paint rectangles”。

现在,您只需查找如下所示的红色矩形:

开发者工具的“Show Paint Rectangles”通过红色矩形提示动画 GIF 问题。
图 3:开发者工具的“Show Paint Rectangles”以红色矩形显示动画 GIF 问题。

屏幕上的红色小方框显示 Chrome 正在重绘某些内容。您知道,其他元素后面隐藏着一个加载器 GIF,因此,当您看到这样的红色框时,需要隐藏可见元素,并检查是否已将动画 GIF 转动。如果已有,则需要弹出一些 CSS 或 JavaScript,以将 display: nonevisibility: hidden 应用于它或其父元素。当然,如果只是背景图片,请务必将其移除。

如果您想在实际网站中查看此行为的示例,请访问 Allegro,其中每个产品的图片都有一个被遮挡(而非显式隐藏)的加载器 GIF。

总结

达到 60fps 意味着执行渲染网页所需的操作,而不执行任何其他操作。消除过度绘制是实现此目标的关键步骤。正在运行的 GIF 动画可能会触发不必要的绘制,您可以使用开发者工具的“Show paint rectangles”工具轻松查找和调试此类绘制。

现在,你没有让那个动画小猫加载器 GIF 一直运行,对吧?