为什么 Chrome 不需要在更改时重新绘制整个图层?
Why does Chrome not need to repaint the entire layer on a change?
我试图在实践中理解 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;
- 在开发工具中打开高亮重绘;
- 打开图层面板;
- 单击黄色按钮切换红色方块的大小;
- 查看突出显示为 "repainted" 的内容,以及当前图层;
- 把红色方块的
position
也改一下试试
我们在包装元素内有两个正方形,一个红色的和一个蓝色的。在 Chrome 的 Layers 面板中,整个内容显示为一层。
如果两个方块都 static
定位,当我转换红色方块的宽度和高度时,我可以看到整个层都被重新绘制,这对我来说是有道理的, 因为, 如果所有 3 个元素都在同一层中, 改变一个的尺寸, 改变整个层的最终结果, 所以整个层必须重新绘制。
如果我将红色方块设置为absolute
定位(你可以通过取消注释.red
样式规则中的行来做到这一点),当我转换它的宽度和高度时,即使开发工具仍然显示一层,只有该层内的红色方块显示为重新绘制。
问题。
第二种情况对我来说没有意义。
如果两个方块和包装元素都在同一层中,改变一个元素不应该影响整个层并导致整个层重新绘制,而不仅仅是红色方块吗?
其他问题。
Chrome,在布局阶段(或确定层的任何阶段)是否将一些元素分离到它们自己的层中(例如由于 position
属性)?这就是为什么它能够分别重新绘制它们的原因吗?是否在合成阶段后转储它们,以便开发人员在开发工具中只能看到一层?
相关背景。
我对现代浏览器的绘画过程的粗略理解如下:
- 在布局阶段,浏览器将 CSS 与 HTML 信息结合起来,以确定屏幕上视觉元素的位置和尺寸。我也相信这是确定有多少渲染层的时间(例如由于
will-change
)。
- 在绘制阶段,浏览器会根据布局数据为每一层创建一个帧缓冲区,其中包含有关每个像素应显示什么颜色的信息。
- 在合成阶段,GPU 根据绘制阶段生成的数据将所有层合成在一起,并将最终结果发送到屏幕。
所以,这里令人困惑的一点是,开发工具的 Paint flashing 仅闪烁从前一帧中无效的图层部分(所以如果一个 absolute
定位的正方形开始变小,帧之间的无效区域是正方形在前一帧中具有的尺寸和坐标的区域)
但是,在内部 整个层 都会重新绘制,无论帧之间的无效部分有多大或多小。
因此,例如,闪烁的光标在 Paint Flashing 上看起来很小,但实际上整个图层都需要重新绘制。
确实,如果我们打开 Performance 面板并启用 Advanced Painting Instrumentation 选项,我们可以看到方形之间的过渡整个图层在问题中描述的两种情况下都被绘制。
来源
https://twitter.com/paul_irish/status/971196975013027840
https://twitter.com/paul_irish/status/971196996924030977
https://twitter.com/paul_irish/status/971197842713800704
一些观察
如果我们要最小化布局和绘画步骤以尽可能减少操作,我们应该将红色方块和黄色按钮分开到它们自己的渲染层。
这种与按钮交互和调整方块大小的方式只会影响它们各自的层,不会导致重绘背景层(包括背景和蓝色方块)。
我试图在实践中理解 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;
- 在开发工具中打开高亮重绘;
- 打开图层面板;
- 单击黄色按钮切换红色方块的大小;
- 查看突出显示为 "repainted" 的内容,以及当前图层;
- 把红色方块的
position
也改一下试试
我们在包装元素内有两个正方形,一个红色的和一个蓝色的。在 Chrome 的 Layers 面板中,整个内容显示为一层。
如果两个方块都
static
定位,当我转换红色方块的宽度和高度时,我可以看到整个层都被重新绘制,这对我来说是有道理的, 因为, 如果所有 3 个元素都在同一层中, 改变一个的尺寸, 改变整个层的最终结果, 所以整个层必须重新绘制。如果我将红色方块设置为
absolute
定位(你可以通过取消注释.red
样式规则中的行来做到这一点),当我转换它的宽度和高度时,即使开发工具仍然显示一层,只有该层内的红色方块显示为重新绘制。
问题。
第二种情况对我来说没有意义。
如果两个方块和包装元素都在同一层中,改变一个元素不应该影响整个层并导致整个层重新绘制,而不仅仅是红色方块吗?
其他问题。
Chrome,在布局阶段(或确定层的任何阶段)是否将一些元素分离到它们自己的层中(例如由于 position
属性)?这就是为什么它能够分别重新绘制它们的原因吗?是否在合成阶段后转储它们,以便开发人员在开发工具中只能看到一层?
相关背景。
我对现代浏览器的绘画过程的粗略理解如下:
- 在布局阶段,浏览器将 CSS 与 HTML 信息结合起来,以确定屏幕上视觉元素的位置和尺寸。我也相信这是确定有多少渲染层的时间(例如由于
will-change
)。 - 在绘制阶段,浏览器会根据布局数据为每一层创建一个帧缓冲区,其中包含有关每个像素应显示什么颜色的信息。
- 在合成阶段,GPU 根据绘制阶段生成的数据将所有层合成在一起,并将最终结果发送到屏幕。
所以,这里令人困惑的一点是,开发工具的 Paint flashing 仅闪烁从前一帧中无效的图层部分(所以如果一个 absolute
定位的正方形开始变小,帧之间的无效区域是正方形在前一帧中具有的尺寸和坐标的区域)
但是,在内部 整个层 都会重新绘制,无论帧之间的无效部分有多大或多小。
因此,例如,闪烁的光标在 Paint Flashing 上看起来很小,但实际上整个图层都需要重新绘制。
确实,如果我们打开 Performance 面板并启用 Advanced Painting Instrumentation 选项,我们可以看到方形之间的过渡整个图层在问题中描述的两种情况下都被绘制。
来源
https://twitter.com/paul_irish/status/971196975013027840
https://twitter.com/paul_irish/status/971196996924030977
https://twitter.com/paul_irish/status/971197842713800704
一些观察
如果我们要最小化布局和绘画步骤以尽可能减少操作,我们应该将红色方块和黄色按钮分开到它们自己的渲染层。
这种与按钮交互和调整方块大小的方式只会影响它们各自的层,不会导致重绘背景层(包括背景和蓝色方块)。