CSS 样式计算与视觉格式化模型
#1 CSS样式计算
CSS样式计算出现在浏览器渲染过程中样式计算
阶段.CSS样式计算的核心目的就是把“一条元素” + “成千上万条样式规则”变成“一组最终生效的绘画指令”。
总的来讲,属性值的计算过程,分为如下这么 5 个步骤:
-
确定声明值
- 在浏览器中,除了作者自己写的CSS样式外,还会有一套浏览器自己的用户样式代理表一套默认样式.这些值如果没有冲突的话,就会应用这些样式.
-
层叠冲突
在确定声明值时,如果出现了样式冲突,那么就需要进入到层叠冲突的流程中.解决层叠冲突的过程主要可以分为以下三步:
-
比较源的重要性
源整体来说大致有三种来源:
- **用户代理样式:**浏览器设定的默认样式
- **页面作者样式:**作者自己写的样式
- **用户样式:**浏览器的用户,可以通过浏览器设置来设置字体大小背景之类的或者浏览器拓展插件的样式文件
源的重要性顺序依次为:页面作者样式 > 用户样式 > 用户代理样式
(
其中 !import 为样式更进阶
)带!import顺序: 页面作者样式!import > 页面作者样式 > 用户样式!import > 用户样式 > 用户代理样式
-
比较优先级
如果说冲突样式是在同一个源里,那么此时我们需要比较两种CSS书写样式的优先级,书写样式的优先级会以选择器的权重来进行比较.
一般来说,选择器的权重遵循 A-B-C-D 组表示,从左到右逐级比较,数字大者胜.
选择器类型 计入哪一位 举例 得分 行内样式 style="…"
A <p style="color:red">
1-0-0-0 ID 选择器 B #header
0-1-0-0 类、属性、伪类 C .nav
,[type="text"]
,:hover
0-0-1-0 元素、伪元素 D div
,::before
0-0-0-1 计算规则:
- 把一条规则里所有选择器各自的 A、B、C 累加。
- 先比 A,再比 B,再比 C;不存在进位(0-1-12 仍小于 0-2-0)。
-
比较次序
- 如果优先级也一样的话,那么只能比较声明的次序了.声明次序靠后的会被优先应用.
-
-
使用继承
- 如果没有声明的属性,会优先考虑继承的属性. 继承的来源为靠元素最近的父元素.而且仅有部分属性可以被继承.
-
使用默认值
- 如果前面都没有用到,那么就会使用默认值.
-
计算值 -> 使用值转换
部分样式是使用的相对单位,比如font-weight:bold`.浏览器内部并不认识bold这个关键字,需要对其映射.因此需要将一些关键字或者相对单位转换成浏览器认识的值或者单位.
#2 视觉格式化模型
#2'0 层叠上下文
用通俗的话来讲,层叠上下文就相当于一个小世界,这个世界中所有的元素都是排好顺序的,然后再把这个小世界拍成一张照片,然后贴到更大的世界中去.
首先,第一个大世界就是根层叠上下文(html),在根层叠上下文中,所有的元素都是有序的进行排列的.
如何有序?
有序的规则就是:背景/边框 < 负zindex元素 < block元素 < float元素 < inline/inline-block元素 < zindex=0元素 < zindex正数元素
如果两个元素师同类型的元素,那么排序按照后来者靠上原则进行.但是他们仍处于同一个层叠上下文中,也就是说如果他们内部有若干个元素组成了一个层叠上下文,那么其先后顺序将在同一个大的层叠上下文中比较,而不是父元素中
例如:
<html> <div id=1></div> <div id=2></div> <div id=3></div> </html>
上述的div样式都是absloute,因为没有产生新的层叠上下文,因此他们之间按照有序规则进行排序.
<html> <div id=1> </div> <div id=2></div> <div id=3></div> </html>
但是这个时候,如果某个元素发生了突变(加入属性:z-index(position!=static),opaacity<0,transform,filter,will-change,flex/grid的子项z-index!=auto
),此时该元素就创建了一个层叠上下文,他的地位就会发生改变,就会从大世界中突出出来.如果他包裹了一些子元素,那么子元素也会突出出来,并且无视有序规则.
#2'1 包含块
包含块决定了元素的尺寸,位置以及某些CSS属性的计算基准.
包含块分为两种:一种是根元素(HTML)所在的包含块.另一种就是非根元素对应的包含块.包括:
- 如果元素的定位为relative/static,那么包含块由离它最近的块容器的内容区域的边缘建立.
- 如果元素的定位为fixed,那么包含块由视口决定.
- 如果元素的定位为absloute,那么包含块由最近的定位为非static的祖先元素的内容区域的边缘建立.
[特殊情况]
如果定位是absloute/fixed,那么包含块也可以由 transform/perspective不是none或者will-change是transform/perspective的内容区域建立.
包含块的计算规则:对于百分比值,例如width:50%是基于对应包含块的内容区域的快读进行计算的.
附:百分比的计算规则:
其中 CB 表示为包含块内容盒(content-box).
属性 基数(以 CB 为参照) 备注 width CB 的 width / height CB 的 height 前提:CB 必须自己有明确高度(非 auto)。如果 CB 高度依赖内容,则 height:% 算出来是 0。 padding CB 的 width 竖直方向也拿宽度算 margin CB 的 width 同样拿宽度算 left CB 的 width 仅当元素是 position:absolute/fixed 时才生效 top CB 的 height 同样要求元素已脱离正常流 transform: translate(50%, 50%) 元素自身的 border-box 尺寸(宽/高) 跟 CB 无关,是“自己”的 50%
#2'2 块级格式上下文 BFC
块级格式上下文(BFC)是一块独立的渲染区域,将里面的内容独立起来,让内部布局和外部互不干扰.但是它自己是外部普通流布局的一部分.
可以触发BFC的几个CSS属性:
- overflow: hidden/auto/scroll
- contain:layout/content
- float:left/right
- display: inline-block,flex,grid,table-cell
- position: absolute/fixed
为什么需要BFC?
1.float脱离文档流,高度塌陷
2.margin重叠
#2'3 视觉格式化模型
视觉格式化模型(Visual Formatting Model)是 CSS 的“总蓝图”:它规定浏览器怎样把一棵元素树,转换成一盒一箱、确定大小与坐标、最终叠到屏幕上的像素。
元素 → 盒子
- 每个元素产生 0~n 个盒子: – 正常元素:1 个主盒子(block / inline / inline-block …)。 – display:none:0 个. – 伪元素、list-item 等可能额外生成匿名盒子。
- 盒子类型决定它接下来进哪条流: block → 普通流/BFC inline-xxx → 行内格式化上下文(IFC) flex-item → Flex 格式化上下文(FFC) grid-item → Grid 格式化上下文(GFC) float / absolute / fixed → 先脱离正常流,再按各自规则定位。
包含块
- 根据规则确定包含块.
- 盒子尺寸计算
- 先算 content 宽/高: – 若是百分比 → 看包含块对应维度。 – 若是 auto → 进入“自动伸缩”算法.
- 再依次加 padding → border → margin。
- 定位与层叠
- 确定层叠上下文.
-
格式化上下文内部排布
-
绘制与合成
- 浏览器把页面切成若干 Layer,生成绘制指令...生成位图...(详见
@浏览器渲染原理
)