CSS布局(三)

369 阅读10分钟

前言

这篇文章已经是CSS布局系列的第三篇文章了。前两篇文章分别介绍了CSS布局的一些相关的概念以及三种盒模型布局。文章链接可以戳这里:

  • CSS布局(一)
  • CSS布局(二) 这篇文章将介绍一种全新的布局方式:弹性盒子(flexbox)布局模型。这种布局方式相较于前面的几种常规布局方式,更具有灵活性,其在大型或者复杂的应用程序中应用比较广泛。由于其灵活性,单个属性必然不能hold住flexbox。因此,flexbox布局模型是一个比较完整的模块。该模块定义了flexbox的相关内容。

什么是 flex-box?

不用说,flex-box肯定是一种布局方式。使用该布局方式,即使不知道视窗大小和元素大小,都可以灵活的调整元素和视窗的大小,将两者进行适应。哇,这话太过于拗口,看着就让人头疼。不要慌,接下来,我们通过和标准盒模型的对比,来解释一波。

弹性盒布局模型 VS 盒模型布局

我们知道,标准盒模型的布局模式,是通过确定盒模型的的尺寸和位置来决定浏览器如何将HTML元素渲染到web页面中的。比如,前面的文章介绍到的块盒子(block-box)和行内盒子(inline-box)。他们的渲染顺序都是按照HTML元素的顺序依次排列,其中,每个元素的大小都已经由CSS属性规定好了。一个一个的放在web页面中。对于标准文档流而言,相关的CSS属性确定了块盒子和行内盒子的大小;他们的默认渲染规则确定了各自的位置。不管是位置还是大小,都是确定的,不可变的。因此,标准的盒模型布局灵活性很差,只要稍微改变视窗的大小,可能整个布局就乱套了。
与标准的盒模型布局相对应,弹性盒子布局模型就显得灵活多了。它的灵活性是由于它的描述规则决定的。flexbox描述的不是盒模型的大小和尺寸,而是一种弹性的布局特征。弹性盒子布局模型由两个部分组成:

  • Flex容器(Flex Container):父元素
  • Flex项目(Flex Item):子元素 flexbox描述的就是父元素和子元素的相对位置关系,并不规定他们的具体大小。因此,灵活性就大大提高了。

开始使用flexbox

flexbox布局的触发很简单。只需要设置一个父子嵌套的html元素结构,然后在父元素声明 display:flex; 就可以了。我们以一个简单的项目列表为例:

  • html:
 <ul>           <!-- 父元素 -->
    <li></li>   <!-- 第一个子元素 -->
    <li></li>   <!-- 第二个子元素 -->
    <li></li>   <!-- 第三个子元素 -->
</ul>  
  • css:
ul {
    display: flex;   /* 或者:inline-flex */
    border: 1px solid #8cacea;
}  

li {
    width: 100px;
    height: 100px;
    background-color: #ccff33;
    margin: 10px;
    list-style: none;
}

最终的显示结果如下图所示: flex-box显示.png 显然,如果不添加display:flex;时,无序列表的排列应该是从上到下排列的。因此,我们已经成功触发flexbox布局。

flexbox中的一些概念

跟盒模型一样,我们首先来了解一下flexbox的一些相关的概念。flexbox的模型如下图所示: flexbox相关概念.png 相关概念分为以下五组:

  • flex容器/flex项目:flex容器是flex布局的起点,即设置了display:flex;的元素;flex项目是指flex容器的直接子元素。
  • 主轴/侧轴:在flex布局中,有一对(两个)相对的概念是至关重要的,它标明了flex-item在flex-container中如何排列。这两个概念就是主轴和侧轴。flex-item按照主轴的方向进行排列。当我们规定了哪一条轴是主轴的时候,侧轴也就随之确定了。
  • 主方向/侧方向:主方向和侧方向分别是主轴和侧轴延伸的方向,即主轴和侧轴的正方向
  • 主轴起点/主轴终点/侧轴起点/侧轴终点:在一个flex布局中,主轴和侧轴的长度是相对固定的,长度固定的坐标轴自然就会存在起点和终点这个概念了。
  • 主轴长度/侧轴长度:显然这两个概念分别是指主轴和侧轴方向容器的长度。

flex-box 相关属性

正如前面所提到的,弹性盒子布局模型是一个完整的模块。既然是一个模块,那么和弹性盒子布局相关的属性就不是一个两个,而是很多个了。这么多属性分别作用于父元素(flex-container)子元素(flex-item) 之上。他们分别规定了对齐、尺寸和方向三个部分。

父元素(flex-container)

作用在父元素之上的属性包括以下几种:

  • 方向:flex-flow -> flex-direction || flex-wrap
  • 对齐:
    • place-content -> align-content || justify-content
    • align-items
  • 尺寸:gap -> row-gap || colomn-gap

方向(direction)

定义flexbox方向的属性包括两个:flex-direction和flex-wrap。其中,flex-direction 定义主轴的方向,即 flex-container中子元素的排列方向;flex-wrap 定义flex-container中的元素是否换行。flex-flow则是两个单独属性的简写模式。

  • flex-direction:该属性的参数有四个:row, row-reverse, column, column-reverse。分别表示主轴方向为 x 轴方向,x 轴逆方向,y 轴方向,y 轴逆方向。其中,row 为flex-direction属性的默认值。
    • 将flex-direction值设置为row,或者不设置: direction-row.png
    • 将flex-direction值设置为row-reverse: direction-row-revserse.png
    • 将flex-direction值设置为column: direction-column.png
    • 将flex-direction值设置为column-reverse: direction-column-reverse.png
  • flex-wrap:该属性的参数值有三个:nowrap, wrap, wrap-reverse。分别控制超过flex-container容量的flex-item元素是否进行换行。nowrap表示不换行,且为该属性的默认值;wrap表示换行;wrap-reverse表示换行,且元素的行的顺序反转。
    • 将 flex-wrap的值设置为 nowrap: wrap-nowrap.png
    • 将 flex-wrap的值设置为 wrap: wrap-wrap.png
    • 将 flex-wrap的值设置为 wrap-reverse: wrap-wrap-reverse.png 除了这两个属性会影响容器中子元素的排列方式以外,还有两个属性会影响到元素的排列:write-mode(horizontal-tb, vertical-rl,vertical-lr)和direction(ltr,rtl)。 这两个属性是通过影响文字的排版方式,进而影响到flex-item的排列方式的。由于不同的语言有不同的排版方式,所以他们需要的flex布局方式也不同。

对齐(alignment)

决定flex-item对齐方式的有三个属性。按照flex-box的概念来看,有一个主轴和一个侧轴;按照css盒模型的观点来看,web页面上有水平方向的x轴和垂直方向的y轴。怎么看都应该是有两个属性来分别控制两个轴的元素的排布方式。对吧?那么,这里为什么会多出来一个属性呢?原因是这样的:在这三个属性中,有一个属性是控制主轴对齐方式的(justify-content), 有两个属性是控制侧轴对齐方式的(align-content, align-items).那么,问题又来了,为什么会出现两个控制侧轴对齐方式的属性呢?这两个控制侧轴对齐方式的两个属性分别是控制单行元素的对齐方式(align-items)和控制多行子元素的对齐方式(align-content)。其中,控制多行对齐方式的属性和主轴对齐方式的属性可以简写成place-content。

  • place-content:这个简写属性,包括justify-content和align-content这两个属性。他们控制元素在flex-container中的排列方式,以及子元素之间的排列间隔
  • align-items:这个属性控制单行元素的对齐方式,即按照那个基准进行对齐或者直接将高度不一的元素进行拉伸保证以其高度相同。 接下来我们来看这三个属性的属性值:
  • 三个属性共有的属性值:
    • flex-start
    • flex-end
    • center
  • justify-content和align-content共有的属性值:
    • space-around
    • space-between
  • align-content和align-items共有的属性值:
    • stretch
  • justify-content独有的属性值:
    • space-evenly
  • align-items独有的属性值:
    • baseline

尺寸(size)

和尺寸相关的属性是用来设置容器中的子元素之间的间隔。另一种代替的方案是设置子元素的margin属性,但是使用margin属性的话,会造成不必要的间隔。

  • 使用 gap 的效果: gap效果.png
  • 使用 margin 的效果: margin效果.png
    从这两个显示的效果的区别可以很容易看出来,使用margin的话,每个元素之间的间隔应该是设置gap的两倍,而且还有和container之间的间距。显然,多出来的间距就是前面提到的不必要的间隔

子元素(flex-item)

作用在子元素的属性也同样包括方向、尺寸和对齐三种:

  • 方向:order -- 通过决定子元素的顺序来确定子元素的位置
  • 尺寸:flex -> flex-frow || flex-shrink || flex-basis -- 有一套完整的计算规则,在下一部分进行介绍
  • 对齐:align-self -- 单个子元素的对齐方式,和align-items很相似,但是它作用的对象是单个子元素

flex-item 宽度计算方法

与flex-item的宽度计算相关的有flex-grow、flex-shrink、flex-basis三个属性。在说明这三个属性的计算方式之前,有两个概念是必须要明白的,一个是flex容器剩余空间,还有一个是flex容器不足空间。

  • flex容器剩余空间:是指放置了flex子元素之后,还剩下的空间
  • flex容器不足空间:是指flex容器的宽度不够,子元素超出容器的部分 两者的对比如下图所示: 剩余空间-不足空间.png 在这三个和子元素宽度相关的属性中,flex-grow是针对剩余空间而言的,flex-shrink是针对不足空间而言的,而flex-basis则要比它们两个属性都更加复杂。flex-basis和flex-item的内容、width、min-width、max-width都有关系。

flex-grow & flex-shrink

首先来看这两个属性的取值情况:

  • flex-grow:0 || number
  • flex-shrink:0 || number 其中,在默认状态下,flex-grow的取值为0,flex-shrink的取值为1。从他们的取值,我们可以看出,这两个属性都表示一个子元素的伸缩比例。接下来,我们来看他们的计算方法:
  • flex-grow: flex-grow计算公式.png
  • flex-shrink: flex-shrink计算公式.png 在这两个属性的计算公式之后的,是flex-item的渲染规则。关于这个问题,大漠老师的一张流程图就已经表示的十分清楚了,我就做个搬运工: flex容器渲染规则.png

flex-basis

flex-basis属性相关的显示结果和width,max-width,min-width都相关。他们之间有一个明确的层级关系。我们继续通过大漠老师的一张流程图来加以说明: flex-basis渲染规则.png 从图中很明显的可以看出这样的层级关系:flex-basis -> width -> content.然后,每一个层级都和min-width、max-width比较。从这几个值中最合适的那个。总结起来就是三个层级,如果设置了最大值和最小值,则进行两次比较。