CSS如何布局排版?正常流和flex流

144 阅读7分钟

正常流

盒模型

在了解正常流之前,或许我们需要先了解一下什么是盒模型(Box Model)。css是通过一个叫做“盒模型”的模型来描述页面上所有的元素,通过设置这个盒子的属性,来调整元素的大小、距离。在盒模型中,每一个元素都有外边距(margin),边框(border),内边距(padding),内容(content)。

截屏2023-07-04 14.18.38.png

Margin

margin定一个元素和其他元素之间的距离,margin是可以为负数的,这样两个元素可能就会重叠。margin有四个方向可以分为上下左右,可以单独设置,也可以一起设置。当设置margin数值时,如果有四个数值就是顺时针上右下左;两个数值就代表上下和左右;当设置auto时,浏览器会给一个适当的值,经常用来让会计元素水平居中。

Border

border定义了元素的边框,边框可以设置粗细、样式、颜色、圆角,或者单独设置一边。大部分元素默认时没有边框的。

Padding

padding定义了元素的内边距,即边框与内容间的距离,padding的值也可以是为负,写法与margin类似,但是属性值没有auto。

Content

一般用width和height来设置内容大小。一般设置的宽或者高是不会计算border和paddig的宽度的,所以当使用border或者padding调整外观样式时很容易改变外观布局,所以对于盒模型css还提供了box-sizing属性,属性值可以为content-box或border-box。content-box是默认设置,指当设置宽高时只包含内容的大小;border-box是指元素设置的宽高是囊括了border和padding的,那么当border或padding的宽度变化时,元素的内容大小会相应地改变,元素整体的占用空间不会更改。

正常布局流

在css标准中,规定了如何排布每一个文字或是盒的算法,这个算法依赖一个排版的“当前状态”,css把这种当前状态称为“格式化上下文(formatting context)”。格式化上下文+盒/文字=位置。

排版

排版的盒分为块级盒和行内级盒,块级盒的默认属性是display: block,行内级盒是display: inline,但是可以通过css改变。排版时需要分别为他们规定块级格式化上下文和行内级格式化上下文,规则就是块级格式化上下文从上往下依次排,行内级格式化上下文从左往右依次排。当正常流排版一个盒盒文字时一般分为以下三种情况:

  • 当遇到块级盒:排入块级格式化上下文
  • 当遇到行内级盒或者文字:首先尝试排入行内级格式化上下文,如果拍不下,那么就创建一个行盒,现将行盒排版,行盒本身是块级盒,但是会创建一个行内级格式化上下文。
  • 当遇到float盒:把盒的顶部与当前行内级格式化上下文上边缘对齐,然后根据float的方向把对应边缘对到块级格式化上下文的边缘,然后重排当前行盒。
定位

实际上,我们还可以通过定位position属性来让元素脱离正常布局流,以下有几种典型position使用场景:

  • relative
    • 元素根据正常流布局
    • 通过top,right,bottom,left偏移其本身的位置,不会影响别的元素
  • absolute
    • 元素脱离正常布局流,后面的元素会当其不存在而占用其原本的空间
    • 元素位置与最近的设置过position: relative的元素相关联,top,right,bottom,left属性就是定位与关联元素的相对位置
  • fixed
    • 元素位置脱离正常布局流,后面的元素会当其不存在而占用其原本的空间
    • 与absolute不同的是,它不与某个元素相关联定位而是与viewport相对

Flex

正常流的排版思路是改变盒的大小,把它放进特定的版面中,而flex的布局理念是改变盒的大小,使他们的结构保持固定。当原始被赋予了display: flex属性后,那么该元素就是flex布局的容器,它的子元素就是flex项。flex项可以设置flex属性,是flex-growflex-shrinkflex-basis的简写,第二个和第三个参数可以被省略,默认值是0 1 auto,但是如果设置了单个的数字值的话flex-basis会变成0%,比如flex: 3,那么实际上展开就是3 1 0%。在这里也简单解释一下flex-growflex-shrinkflex-basis这三个属性。

flex-grow是定义了一个子元素在必要时的增长能力,它接受一个没有单位的数值作为比例,这个比例决定了在容器中占据的空间,比如数值是2的话,我就会理解为在容器的可支配空间中,该子元素在所有子项中占据两份的空间。相应地,flex-shrink定义的是子项收缩的能力。flex-basis定义的是在分配空间之前默认的尺寸,它可以是一个长度,也可以是一个关键字。关键字“auto”代表看width或者height属性;关键字“content”意味着尺寸看元素的内容决定。如果设置能,周围额外的空间就不会被考虑进去,如果设置auto,额外的空间就根据flex-grow的值决定。

那么我们就来看看flex布局是如何开始排版的?

第一步,分行。 在容器中,flex-direction属性决定了整个布局的排列方向,一共有四个可选项row,row-reverse,column,column-reverse,分别是从左到右,从右到左,从上到下,从下到上。flex-wrap决定了整个布局是否会换行,nowrap指所有的子项都在一行,wrap是指可以换行,从上到下排行,wrap-reverse是从下到上。

flex-direction属性定义的方向就是主轴方向,如果flex-wrap不允许换行,那么所有子项都被放在同一行;如果允许换行,那么主轴的剩余空间就是容器的主轴尺寸,每放进去一个子项就剩余空间就要剪掉此子项的主轴的尺寸,直到下一个子项放不进去换行,在这里我们要注意如果子项有flex属性,我们也可以看到它的flex-basis是0%,那么此子项就会被认为主轴尺寸为0,一定可以放进当前行。在分行过程中,还顺便会对每一行计算交叉轴尺寸和主轴剩余空间,交叉轴尺寸就是本行所有交叉轴尺寸的最大值。

第二步,计算主轴上子项的尺寸和位置。 如果不允许换行,并且主轴尺寸超出了容器,那么就要等比缩放。如果允许换行,就把主轴的剩余空间按照flex比例分配给有flex属性的子项。

如果没有子项有flex属性,或者已经放到大了最大值,那么就需要在容器上使用justify-content属性来分配主轴上的剩余空间。最常用的有center:左右两边的空间一样大;space-between:每个子项中间的空间一样大,左右贴边;space-around,每个子项中间的空间一样大,左右是子项间距的一半;space-evenly:每个子项中间的空间一样大,包括左右两边。

第三步,计算交叉轴上子项的尺寸和位置。 首先要计算容器交叉轴的剩余空间如何分布,在容器上添加align-content属性,分布类似于justify-content的值。

截屏2023-07-05 16.38.43.png 截屏2023-07-05 16.39.09.png 截屏2023-07-05 16.38.54.png

其次就是要计算在每一行的交叉轴上剩余空间如何分布,这取决于容器的align-items属性和子项的align-self属性。这两个属性的值也和justify-content属性差不多,但是还有一个要注意的值时stretch,是说会把子元素抻大到填满当前行的交叉轴尺寸,如上所述,交叉轴尺寸取决于当前行所有子元素交叉轴尺寸的最大值。


那么现在知道css如何布局排版了吗?

Reference:
time.geekbang.org/column/arti… time.geekbang.org/column/arti… pushi.simon1987.com/css-box-mod… css-tricks.com/snippets/cs…