当前位置: 首页 > 科技观察

新的CSS特性contain控制页面重绘和重排

时间:2023-03-17 10:29:51 科技观察

在介绍新的CSS属性contain之前,读者需要了解什么是页面重绘和重排。发现之前已经描述过很多次了,大家可以看看这个提高CSS动画性能的正确姿势[1]。OK,进入本文正题,为什么包含?contain属性允许我们指定一个特定的DOM元素及其子元素,这样它们就可以独立于整个DOM树结构。目的是使浏览器能够只重绘和重新排列某些元素,而不必每次都以整个页面为目标。contain属性允许作者指示元素及其内容尽可能独立于文档树的其余部分。这允许浏览器为DOM的有限区域而不是整个页面重新计算布局、样式、绘画、大小或它们的任意组合。contain语法看它的语法:{/*Nolayoutcontainment.*/contain:none;/*Turnoncontainmentforlayout,style,paint,andsize.*/contain:strict;/*Turnoncontainmentforlayout,style,andpaint.*/contain:content;/*Turnonsizecontainmentforanelement.*/contain:size;/*Turnonlayoutcontainmentforanelement.*/contain:layout;/*Turnonstylecontainmentforanelement.*/contain:style;/*Turnonpaintcontainmentforanelement.*/contain:paint;}排除none,还有6个值,我们一一来看。contain:sizecontain:size:设置了contain:size的元素的渲染不会受到其子元素内容的影响。该值打开元素的尺寸限制。这确保了可以在不需要检查其后代的情况下布置包含框。开始看到这个定义也是一头雾水,光看定义很难理解是什么意思。还需要练习:假设我们有如下简单的结构:5px;font-size:14px;}而且,在jQuery的帮助下,每次点击容器都会添加一个

Coco

结构:$('.container').on('click',e=>{$('.container').append('

Coco

')})然后会得到如下结果:可以看到container.container的高度会随着增加元素,这是正常现象。这时我们在容器.container中添加一个contain:size,上面说到:设置了contain:size的元素的渲染不会受到其子元素内容的影响。.container{width:300px;padding:10px;border:1pxsolidred;+contain:size}看看会发生什么:正常情况下,父元素的高度会因为子元素的增加而升高,但是现在,子元素元素的变化不再影响父元素的样式布局,这就是contain:size的作用。contain:style接下来说一下contain:style、contain:layout和contain:paint。先看看contain:样式。在撰写本文时,contain:样式已被暂时删除。CSSContainmentModuleLevel1[2]:从本规范中删除有风险的“样式包含”功能,将其移至Level2。好吧,官方说是因为某些风险而暂时将其删除。在规范的第二版中可能会重新定义,所以暂时搁置该属性。contain:paintcontain:paint:一个带有contain:paint的元素设置了布局约束,意思是通知UserAgent这个元素的子元素不会显示在这个元素的边界之外。因此,如果元素在屏幕外或以其他方式设置为不可见,它也保证其后代不可见且不被渲染。该值打开元素的绘制包含。这确保了包含框的后代不会显示在其边界之外,因此如果一个元素在屏幕外或以其他方式不可见,它的后代也保证不可见。这个稍微好理解一点。先来看第一个特点:设置了contain:paint的元素的子元素不会显示在这个元素的边界之外。设置了contain:paint的元素的子元素将不会在这个元素的border之外显示的特性有点类似于overflow:hidden,就是明确的告知useragent子元素的内容将会不超过元素的边界,所以超出的部分不需要渲染。简单的例子,假设元素结构如下:

Coco

.container{contain:paint;border:1pxsolidred;}p{left:-100px;}看一下,设置contain:paint和不设置时会发生什么:CodePenDemo--contain:paintDemo[3]设置了contain:paint的元素在屏幕外时不会被渲染。通过使用contain:paint,如果元素在屏幕外,用户代理会忽略这些元素的渲染,从而允许更快地渲染其他内容。contain:layoutcontain:layout:一个带有contain:layout的元素设置了一个布局约束,意思是通知UserAgent元素内部的样式变化不会引起元素外部的样式变化,反之亦然。该值打开元素的布局包含。这确保了包含框出于布局目的是完全不透明的;外部的任何东西都不能影响它的内部布局,反之亦然。启用contain:layout可以潜在地将每帧需要渲染的元素数量减少到少数,而不是重新渲染整个文档,从而为浏览器节省大量不必要的工作并显着提高性能。使用contain:layout,开发人员可以指定对该元素的任何后代的任何更改都不会影响任何外部元素的布局,反之亦然。所以浏览器只计算内部元素的位置(如果它被修改),其余的DOM保持不变。所以这意味着帧渲染管道中的布局过程会更快。存在的问题描述的很好,但是在实际的Demo测试中(截至2021/04/27,Chrome90.0.4430.85),仅仅单独使用contain:layout并没有验证到上述好的效果。如果设置了contain:layout的指定元素,则对该元素的任何后代的任何更改仍将影响任何外部元素的布局。点击红框会添加一个

Coco

元素插入到容器中:简单代码如下:

Coco

...
html,body{width:100%;height:100%;显示:flex;justify-content:center;align-items:center;flex-direction:column;gap:10px;}.container{width:150px;padding:10px;contain:layout;border:1pxsolidred;}.g-test{width:150px;height:150px;border:1pxsolidgreen;}CodePenDemo--contain:layoutDemo[4]CaniUse--CSSContainupto2021-04-27,CSSContaincompatibilityonCaniUse,就可以开始使用了:参考CSSContainmentModuleLevel1[5]CSSContainment[6]Chrome52中的CSSContainment[7]最后,本文到此结束,希望对你有所帮助:)更多精彩的CSS技术文章汇总在我的Github——iCSS[8],持续更新。欢迎点个星订阅收藏。有什么问题或者建议可以多交流。原创文章文笔有限,知识匮乏。如果文章中有任何不准确的地方,请告诉我。参考资料[1]提高CSS动画性能的正确姿势:https://github.com/chokcoco/iCSS/issues/11[2]CSSContainmentModuleLevel1:https://www.w3.org/TR/css-contain-1/[3]CodePenDemo--包含:paintDemo:https://codepen.io/Chokcoco/pen/KKwmgmN[4]CodePenDemo--包含:layoutDemo:https://codepen.io/Chokcoco/pen/rNjRELL[5]CSS包含模块级别1:https://www.w3.org/TR/css-contain-1/[6]CSS包含:https://justmarkup.com/articles/2016-04-05-css-containment/[7]Chrome52中的CSSContainment:https://developers.google.com/web/updates/2016/06/css-containment[8]Github--iCSS:https://github.com/chokcoco/iCSS