为啥 Chrome 不需要在更改时重新绘制整个图层?

Posted

技术标签:

【中文标题】为啥 Chrome 不需要在更改时重新绘制整个图层?【英文标题】:Why does Chrome not need to repaint the entire layer on a change?为什么 Chrome 不需要在更改时重新绘制整个图层? 【发布时间】:2018-08-12 14:27:01 【问题描述】:

在实践中,我试图了解 Chrome 的布局 → 绘制 → 复合管道是如何工作的。在我的测试过程中,我对 Chrome 在以下情况下的行为感到困惑。 (Codepen)

var button = document.querySelector('button');
var red = document.querySelector('.red');
var blue = document.querySelector('.blue');
button.addEventListener('click', function() 
  red.classList.toggle('test');
)
.wrapper 
  height: 100%;
  width: 100%;
  background-color: teal;
  position: static;


.square 
  height: 200px;
  width: 200px;
  position: static;
  transition: transform 0.3s ease,
    opacity 0.3s ease,
    width 0.3s ease,
    height 0.3s ease,
    box-shadow 0.3s ease;


.red 
  /* position: absolute; */
  background-color: red;
  top: 100px;
  left: 100px;
  /* will-change: transform; */
  opacity: 1;


.blue 
  background-color: blue;
  z-index: 3;


.test 
  /* transform: translate3d(50px, 50px, 0); */
  /* opacity: 0; */
  width: 60px;
  height: 60px;
  /* box-shadow: 0px 0px 0px 10px rgba(0,0,0,.5) */


button 
  position: fixed;
  right: 100px;
  top: 50px;
  z-index: 10000;
  font-weight: bold;
  background-color: yellow;
  padding: 5px 10px;
  /* will-change: transform; */
<div class="wrapper">
  <div class="red square"></div>
  <div class="blue square"></div>
</div>
<button>Click</button>

复制。

使用 Chrome; 在开发工具中打开高亮重绘; 打开图层面板; 点击黄色按钮切换红色方块的大小; 查看突出显示为“重绘”的内容以及当前图层; 把红色方块的position也改一下试试。

我们有两个正方形,一个红色的和一个蓝色的,在一个包装元素内。在 Chrome 的 Layers 面板中,这整件事显示为一层。

1234563元素在同一层,改变一个的尺寸,改变整个层的最终结果,所以必须重新绘制整个层。

如果我将红色方块设置为absolute 定位(您可以通过取消注释.red 样式规则中的行来做到这一点),当我转换它的宽度和高度时,即使开发工具仍然显示一层,只有该层内的红色方块显示为重新绘制。

问题。 第二种情况对我来说没有意义。

如果两个方块和包装器元素都在同一层,不应该改变一个元素影响整个图层并导致整个图层重新绘制,而不仅仅是红色方块?

其他问题。 Chrome 是否在布局阶段(或确定层的任何阶段)将一些元素分成它们自己的层(例如,由于position 属性)?这就是为什么它能够单独重新粉刷它们吗?是否在合成阶段之后转储它们,因此开发人员只能在开发工具中看到一层?

相关背景。 我对现代浏览器的绘制过程粗略理解如下:

在布局阶段,浏览器将 CSS 与 html 信息相结合,以便确定屏幕上可视元素的位置和尺寸。我也相信这是它决定有多少渲染层的时间(例如,由于will-change)。 在绘制阶段,浏览器会根据布局数据为每个图层创建一个帧缓冲区,其中包含有关每个像素应显示什么颜色的信息。 在合成阶段,GPU 根据绘制阶段生成的数据将所有图层合成在一起,并将最终结果发送到屏幕。

【问题讨论】:

您需要在您的问题中发布一个完整但最小的问题标记或代码示例,以便我们复制问题,而不是 Codepen 或任何其他第三方网站,:minimal reproducible example 你能澄清一下你的问题吗?您是在问整个过程是如何进行的吗? (需要几本书来解释这一切),还是你真的只关心你的position: absolute 案例? (如果是这样,在网上简单搜索一下就足够了:w3.org/TR/CSS2/visuren.html#absolute-positioning) @Kaiido 我试着澄清一下。是的,我想我是在询问整个过程,position: absolute 只是一个例子。我的问题的本质是为什么我只看到整个图层中的一个元素被重新绘制而不是整个图层。也许我问错了,我不知道。 OP 询问了这个问题on meta。 (/cc @Rob) 【参考方案1】:

所以,这里令人困惑的一点是开发工具的 Paint flashing 仅闪烁从前一帧无效的图层部分(所以如果一个 absolute 定位正方形开始变小,帧之间的无效区域是具有正方形在前一帧中的尺寸和坐标的区域)

但是,无论帧之间的无效部分有多大或多小,整个层都会在内部重新绘制。

因此,例如,闪烁的光标在 Paint Flashing 上看起来很小,但实际上需要重新绘制整个图层。

确实,如果我们打开 Performance 面板并启用 Advanced Painting Instrumentation 选项,我们可以看到在方形过渡之间整个图层都被绘制了在问题中描述的两种情况下

来源

https://twitter.com/paul_irish/status/971196975013027840https://twitter.com/paul_irish/status/971196996924030977https://twitter.com/paul_irish/status/971197842713800704

一些观察

如果我们要最小化布局和绘画步骤以尽可能少地进行操作,我们应该将红色方块和黄色按钮分开到它们自己的渲染层。

这种方式与按钮交互并调整方块大小只会影响它们各自的图层,不会导致背景图层(包括背景和蓝色方块)的重绘。

【讨论】:

感谢您在此处发布 Chrome 开发人员的答案,但请让他们下次在 *** 中回复。我们从真正知情的人那里得到的间接答案越少,整个社区就能受益越多! 我已经联系了他。由于无法保证他实际上会自己发布答案,因此现在必须这样做。 @DimitrisKaragiannis 这是否意味着当页面中的所有元素都在一个根层中时,每次在该层上发生视觉变化时都会绘制所有元素?假设悬停按钮上的背景颜色?这对我来说听起来很疯狂。 @behnil 没错!您可以打开 devtools 进行尝试并亲自查看。但是对于简单的页面应该没问题,浏览器速度很快。如果您开始注意到口吃,您绝对应该尝试优化。 @DimitrisKaragiannis 我仍然无法相信整个页面(在这种情况下为图层)需要重绘。查看这篇文章,尤其是关于矩形失效的部分。 hacks.mozilla.org/2017/10/…

以上是关于为啥 Chrome 不需要在更改时重新绘制整个图层?的主要内容,如果未能解决你的问题,请参考以下文章

为啥组件在单击和状态更改时会重新渲染?

如何强制 Android Webview 在 DOM 更改上重新绘制?

为啥在jsp更改时tomcat不需要重启

为啥不重新压缩整个文件就不能修改 zip 文件?

为啥这个 CoreGraphics 绘制代码很慢?

为啥过渡框阴影会导致整页重绘?