图解CSS:Flexbox布局(Part2)

发布于 大漠

如果你未阅读第一部分,强烈建议您先花点时间阅读第一部分

运用于Flex项目属性

运用于Flex项目中的CSS属性和运用于Flex容器上的属性类似,主要分为三个部分:对齐尺寸方向 三个部分:

align-self

在Flex容器中可以使用align-items来控制来控制Flex项目(所有Flex容器的子元素)在Flex容器侧轴方向的对齐方式。在Flexbox布局模块中,还可以使用align-self对单个Flex项目进行对齐方式的控制,同样是Flex容器侧轴方向的对齐。

align-self属性可接受的值主要有:

align-self: auto | flex-start | flex-end | center | baseline | stretch;

即使在Flex容器上显式设置了align-items用来控制所有Flex项目在Flex容器侧轴上的对齐方式,但只要在Flex项目上显式设置了align-self的值,则会覆盖align-items属性。只不过只对显式设置了align-self的Flex项目生效。比如下面这个示例,我们在Flex容器显式设置了align-items的值为center,并且对单数Flex项目显式设置align-self的值:

.flex__container {
    align-items: center;
    direction: ltr;
    writing-mode: horizontal-tb;
    flex-flow: row nowrap;
}

.flex__item:nth-child(odd) {
    algin-self: var(--flexSelf);
}

你将看到的效果如下:

这里需要特别提出的一点是:Flex项目的align-self显式设置值为auto时不会覆盖Flex容器的align-items;另外如果在Flex项目上显式设置margin的值为auto时,Flex项目的align-self值将会失效

同样的,algin-self也会受directionwriting-mode取值的影响。

当Flex容器中有多行时,每一行会进行独立的布局,其中align-self属性每次仅作用于单独一行上的Flex项目;同时每一行侧轴长度指定为可容纳该行中的Flex项目(在依据align-self属性进行对齐之后)的最小值,并且这些行依据align-content属性在Flex容器中进行对齐。

order

在Flexbox布局模块中,我们除了在Flex容器上通过flex-direction来改变DOM源的顺序之外,还可以在Flex项目上显式设置order属性来改变DOM源顺序:

order: 0 || <number>

其中0order的默认值,同时它可以接受正负值(整数值)。

比如上面示例中,每组中的第二个Flex项目order设置了不同的值:

  • 第一组中Flex项目未显式设置order值(即默认值为0
  • 第二组中第二个Flex项目显式设置order的值为1,这个时候该Flex项目会排列在最末尾
  • 第三组中第二个Flex项目显式设置order的值为-1,这个时候该Flex项目会排列在最前面

其中order值越大,Flex项目越后;反之越前:

order用来控制Flex项目顺序在布局中非常的实用,比如我们要构建一个多列布局:

<!-- HTML -->
<header>Header Section</header>
<main>
    <article>Article Section</article>
    <nav>Nav Section</nav>
    <aside>Aside Section</aside>
</main>
<footer>Footer Section</footer>

其中<article>放在<nav><aside>前面,主要是为了内容为先。针对这样的DOM结构,如果我们希望<nav><article>左侧,<aside><article>右侧时,order属性就可以起关键性的作用:

上面的示例中,在main:hover改变了<nav><aside>两个元素的order值:

nav {
    order: -1;
}

main:hover nav {
    order: 1;
}

main:hover aside {
    order: -1;
}

这个时候看到的效果如下:

flex

flex属性主要用来让Flex项目如何伸长或缩短以适应Flex容器中的可用空间。它是一个简写属性,即:

flex: [<flex-grow><flex-shrink>?||<flex-basis>]

其中:

flex-grow: <number> 
flex-shrink: <number>
flex-basis: content | <width>

这几个属性都有其初始值:

  • flex-grow的初始值为0
  • flex-shrink的初始值为1
  • flex-basis的初始值为auto

flex的初始值为0 1 auto。换句话说,如果需要重置flex属性的话,可以将其值设置为none。它的实际语法是:

flex: none | [ <'flex-grow'> <'flex-shrink'>? || <'flex-basis'> ]

在Flexbox布局模块中,flex是一个复杂的属性,从上面的语法规则上不难发现,flex属性可以指定 1个值(单值语法)2个值(双值语法)3个值(三值语法)

单值语法:值必须为以下其中之一:

  • 一个无单位的数(<number>,比如flex: 1,这个时候它会被当作<flex-grow>的值
  • 一个有效的宽度(width)值,比如flex: 30vw,这个时候它会被当作<flex-basis>的值
  • 关键词 noneautoinitial(即初始值

有关于initial更详细的介绍,可以阅读《图解CSS:CSS层叠和继承》一文。

双值语法:第一个值必须为一个无单位数值,并且它会被当作<flex-grow>的值;第二个值必须为以下之一:

  • 一个无单位的数(<number>,它会被当作<flex-shrink>的值
  • 一个有效的宽度(width)值,它会被当作<flex-basis>的值

比如:

flex: 1 2 » <flex-grow> <flex-shrink> » flex-grow: 1; flex-shrink: 2
flex: 2 30vw » <flex-grow> <flex-basis> » flex-grow: 2; flex-basis: 30vw

三值语法

  • 第一个值必须是一个无单位数(<number>,并且它会被当作<flex-grow>的值
  • 第二个值必须是一个无单位数(<number>,并且它会被当作<flex-shrink>的值
  • 第三个值必须为一个有效的宽度(width)值,并且它会被当作<flex-basis>的值

比如:

flex: 1 2 30vw » <flex-grow> <flex-shrink> <flex-basis> 
//等效于
flex-grow: 1;
flex-shrink: 2;
flex-basis: 30vw;

综合上述,flex属性的取值可以是:

  • auto:Flex项目会根据自身的widthheight来确定尺寸,但Flex项目根据Flex容器剩余空间进行伸缩。其相当于flex: 1 1 auto
  • initial:Flex项目会根据自身的widthheight来设置尺寸。它会缩短自身以适应Flex容器,但不会伸长并吸收Flex容器中的额外剩余空间来适应Flex容器。其相当于flex: 0 1 auto
  • none:Flex项目会根据自身的widthheight来设置尺寸。它是完全非弹性的(既不会缩短,也不会伸长来适应Flex容器)。其相当于flex: 0 0 auto
  • <flex-grow>:定义Flex项目的flex-grow属性,取值为<number>
  • <flex-shrink>:定义Flex项目的flex-shrink属性,取值为<number>
  • <flex-basis>:定义Flex项目的flex-basis属性。若值为0,则必须加上单位,以免被视作伸缩性

在大多数情况下,开发者需要将flex设置为autoinitialnone或一个无单位的正整数。比如下面这个示例,你可以尝试着调整Flex容器的大小,来观察flex取这些值的差异:

在上面示例的基础上,我们可以给每个Flex项目的flex设置三个值:

事实上,flex在Flexbox布局模块中是一个复杂的属性,为了更好的向大家阐述清楚该属性,接下来主要分三个部分来阐述。即flex的三个子属性:flex-grow(扩展比率)、flex-shrink(收缩比率)和 flex-basis(伸缩基准)。这三个属性可以控制Flex项目,具体的表现如下:

  • flex-grow:设置Flex项目的扩展比率,让Flex项目得到(扩展)多少Flex容器剩余空间(Positive Free Space),即Flex项目可能会变大
  • flex-shrink:设置Flex项目收缩比率,让Flex项目减去Flex容器不足的空间(Negative Free Space),即Flex项目可能会变小
  • flex-basis:Flex项目未扩展或收缩之前,它的大小,即指定了Flex项目在主轴方向的初始大小

也就是说,flex-growflex-shrinkflex-basis可以用来指定或改变Flex项目的大小。在具体介绍这几个属性之前,先给大家介绍几个概念。

剩余空间和不足空间

在Flexbox布局模块中,Flex容器中可能会包含一个或多个Flex项目。而Flex容器和Flex项目都有其自身的尺寸大小,那么就会有Flex项目尺寸大小之和大于或小于Flex容器的情景:

  • 当所有Flex项目尺寸大小之和小于Flex容器时,Flex容器就会有多余的空间没有被填充,那么这个空间就被称为 Flex容器的剩余空间(Positive Free Space)
  • 当所有Flex项目尺寸大小之和大于Flex容器时,Flex容器就没有足够的空间容纳所有Flex项目(Flex项目会溢出Flex容器),那么多出来的这个空间就被称为不足空间(Negative Free Space),也被称为负空间

举个例子向大家阐述这两种情形:“假设我们有一个Flex容器,显式给其设置了width80vw,并且该Flex容器中包含了四个Flex项目,每个Flex项目的width10vw”。这个时候所有Flex项目的宽度总和则是10vw x 4 = 40vw,那么Flex容器将会有剩余空间,即80vw - 40vw = 40vw。这个 40vw 就是Flex容器的剩余空间:

再来看第二种情形:“假设Flex容器width40vw,并且每个Flex项目的width20vw”。这个时候所有Flex项目的宽度总和则是20vw x 4 = 80vw,那么Flex容器则没有足够的空间容纳Flex项目(Flex项目会溢出Flex容器),即40vw - 80vw = -40vw。这个 **40vw**就是我们所说的Flex容器的不足空间:

上面演示的是主轴在x轴方向,如果主轴变成y轴的方向,同样存在上述两种情形,只不过width变成了height

CSS尺寸属性和值

如果你有阅读过《图解CSS: 元素尺寸的设置》、《图解CSS:CSS逻辑属性》以及《CSS 盒模型》的话,你会发现在CSS中可以通过不同的属性以及不同的值来设置元素大小。比如,在CSS中我们可以通过下面这些属性给Flex项目显式指定尺寸:

能用于上图中属性的值,可以是下面这些:

  • auto:设置值为auto时,容器的大小将会以容器的内容来计算,它将是一个自动大小;不过给min-widthmin-heightmin-inline-sizemin-block-size设置值为auto时,将会指定一个自动计算好的最小值
  • none:如果取值为none时,元素盒子的大小是没有任何限制
  • <length-percentage>:使用<length><percent>指定元素的大小,在适当情况下,根据其父元素的宽度来解析百分比
  • min-content:如果指定了内联轴,那么min-content对应的大小则是内联大小,否则将表现为属性的初始值,即固有的最小宽度
  • max-content:如果指定了内联轴,那么max-content对应的大小则是内联大小,否则将表现为属性的初始值,即固有的首选宽度
  • fit-content():如果显式指定了内联轴,使用fit-content()函数,可以用指定的参数替换可用空间,即min(max-content, max(min-content, <length-percentage>));否则将表现为属性的初始值。对于内在尺寸,fit-content(<length>)表现长度值(<length>)。如果fit-content()使用了百分比值,将会作为min-content当作最小内容,max-content作为最大内容

而其中automin-contentmax-contentfit-content()又被称为自动计算尺寸大小方式

在CSS中除了widthheightinline-sizeblock-size会影响元素尺寸大小之外,还有运用于元素盒子的属性,比如内距,边框等都会影响元素尺寸大小。有关于这方面更详细的介绍可以阅读《CSS 盒模型》一文。

内部尺寸和外部尺寸

熟悉CSS盒模型的同学都知道,根据盒子不同的边缘可以将盒子划分成不同类型的盒子,比如content-boxpadding-boxborder-boxmargin-box

其中content-box对应的尺寸被称为内部尺寸,而margin-box对应的尺寸被称为外部尺寸

明确的尺寸和不确定的尺寸

明确的尺寸指的是不需要执行布局就可以确定盒子的大小。也就是说,显式给容器设置一个固定值、或内容所占区域的大小、或一个容器块的初始大小,或通过其他计算方式得到的尺寸(比如Flexbox布局中的“拉伸和压缩(Strretch-fit)”)。

不确定的尺寸指的是一个未知的尺寸。换句话容器的大小具备无限空间(即有可能很大,也有可能很小)。

通俗一点来理解的话,明确的尺寸是知道容器的width(或inline-size)和height(或block-size);不确定的尺寸是需要根据内容来计算的,所以要知道不确定的尺寸需要先检查内容。

有了上述概念之后,我们就可以开始进入flex-growflex-shrinkflex-basis三个属性的世界。

flex-grow

flex-grow的语法规则很简单:

flex-grow: <number>

其中<number>是一个正数的数值,并且其默认值是0

在Flexbox布局模块中,flex-grow是一个扩展因子,也被称为扩展比例。其意思是,当Flex容器有一定的剩余空间时,flex-grow可以让Flex项目分配Flex容器剩余的空间,每个Flex项目将根据flex-grow因子扩展,从而让Flex项目占满整个Flex容器(有效的分配和利用Flex容器剩余空间)。

flex-grow的值是一个大于0的值时,Flex项目就会占用Flex容器剩余空间。在Flexbox布局模块中,在使用flex-grow时可以按下面的方式使用:

  • 所有Flex项目设置相同的flex-grow
  • 每个Flex项目设置不同的flex-grow

正如上例所示,在Flex项目中设置flex-grow之后,Flex项目会根据Flex容器宽度来动态调整自身的尺寸大小:

当Flex容器没有任何剩余空间时,即使Flex项目显式设置了flex-grow的值,Flex项目也不会扩展(因为没有空间可扩展):

正如上图所示,flex-grow的不同设置得到的效果将会不一样,但flex-grow的值始终总量为1,也就是说,所有Flex项目的占有量总和(分子)和分母相同。

如果你希望能更彻底的了解flex-grow,那就需要了解flex-grow的计算方法。flex-grow有相应的计算公式:

下面用一个简单的示例来阐述flex-grow是如何分配Flex容器的剩余空间:

<!-- HTML -->
<div class="flex__container">
    <div class="flex__item" style="--flexGrow: 0">flex-grow:0</div>
    <div class="flex__item" style="--flexGrow: 1">flex-grow:1</div>
    <div class="flex__item" style="--flexGrow: 2">flex-grow:2</div>
    <div class="flex__item" style="--flexGrow: 3">flex-grow:3</div>
</div>

/* CSS */

:root {
    --flexGrow: 0;
}

.flex__container {
    inline-size: 80vw;
}

.flex__item {
    flex-grow: var(--flexGrow);
    inline-size: 10vw;
}

效果如下:

从该示例的代码中我们可以获知:

  • Flex容器的宽度是 80vw (CSS中设置了inline-size: 80vw)
  • Flex容器中共有四个Flex项目,并且每个Flex项目的宽度是 10vw(CSS中设置了inline-size: 10vw
  • Flex项目宽度总和为10vw x 4 = 40vw
  • Flex容器的剩余空间为80vw - 40vw = 40vw
  • Flex项目的flex-grow的值分别是0123,所有Flex项目的flex-grow总和为0 + 1 + 2 + 3 = 6

即:

flex-grow公式中变量名称 Flex项目1 Flex项目2 Flex项目3 Flex项目4 总数
Flex项目的flex-grow 0 1 2 3 0 + 1 + 2 + 3 = 6
Flex项目的宽度(width) 10vw 10vw 10vw 10vw 10vw x 4 = 40vw
Flex容器的宽度(width)         80vw
Flex容器的剩余空间         80vw - 40vw = 40vw
Flex项目新宽度(width) ? ? ? ?  

有了这些参数值之后,根据上面提供的flex-grow计算公式就可以计算出每个Flex项目的宽度:

» 计算之后的Flex项目宽度 = (Flex项目的flex-grow值 ÷ 所有Flex项目的flex-grow总值 × Flex容器剩余空间 ) + Flex项目初始宽度

» 计算之后的Flex1项目宽度 = (0 ÷ 6 × 40 ) + 10 = 10vw
» 计算之后的Flex2项目宽度 = (1 ÷ 6 × 40 ) + 10 = 16.667vw
» 计算之后的Flex3项目宽度 = (2 ÷ 6 × 40 ) + 10 = 23.333vw
» 计算之后的Flex4项目宽度 = (3 ÷ 6 × 40 ) + 10 = 30vw

具体如下图所示:

上面的示例中flex-grow的取值都是正整数值。事实上,flex-grow还可以设置为小数值。在上例的基础上,将Flex项目的flex-grow值分别设置为00.10.20.3,此时所有Flex项目的flex-grow总和为0.6。虽然取值为小数,但其计算方式是一样的,只不过所有flex-grow值的总和小于1时,Flex容器的剩余空间不会完全分配完(最终还会有剩余的空间)。换句话说,就该例而言,Flex项目只分完了Flex容器剩余空间的60%。最终的效果如下:

虽然在Flex项目中显式设置了flex-grow的值(大于或等于1)时,Flex项目会根据相应的比率得到Flex容器剩余空间,Flex项目宽度会得到扩展(变宽),但这并不是绝对的,如果Flex项目上显式设置了max-widthmax-inline-size时,Flex项目会受到它们的限制。比如下面这个示例,我们显式在Flex项目中设置max-inline-size: 18vw,得到的效果如下:

在上面的示例中,为了更好的向大家演示flex-grow的计算,在代码中给Flex项目显式的设置了inline-size的值为10vw(相当于设置了flex-basis值)。后面我们会详细的介绍flex-basis的使用。从示例的效果中不难发现,flex-growinline-size(或flex-basis)是会相互影响的。这也令Flex项目计算变得复杂化了,尤其是加入min-widthmin-inline-sizemax-widtmax-inline-size会更为复杂。

为什么这么说呢?我们用一个示例来举例。比如说,不在Flex项目上显式的设置inline-sizewidthflex-basis的值。根据我们所掌握的CSS知识可以得知,这个时候,Flex项目的大小会取决于其内容的大小(相当于inline-sizewidth设置了max-content)。

示例中代码很简单:

:root {
    --flexGrow: 1;
}

.flex__container {
    inline-size: 80vw;
}

.flex__item {
    flex-grow: var(--flexGrow);
}

示例中的四个Flex项目的flex-flow的值都是相同的,即1。Flex容器的宽度设置的是80vw,具体渲染出来的容器宽度和浏览器视窗有关联,同时Flex项目并未显式指定具体宽度的属性,那么其宽度会根据自身内容来决定。为了更好的向大家演示,通过浏览器调试器,将对应元素的盒模型尺寸截图示意:

从上图可以得到:

  • Flex容器的宽度是804px
  • Flex项目的宽度分别是43.36px92.09px140.83px189.56px,所有Flex项目宽度的总和为465.84px
  • Flex容器的剩余宽度为804px - 465.84px = 338.16px
  • 所有Flex项目的flex-grow值为1

将相应的值套用到flex-grow的公式中,可以得到:

» 计算之后的Flex项目宽度 = (Flex项目的flex-grow值 ÷ 所有Flex项目的flex-grow总值 × Flex容器剩余空间 ) + Flex项目初始宽度

» 计算之后的Flex1项目宽度 = (1 ÷ 4 × 338.16) + 43.36 = 127.9px
» 计算之后的Flex2项目宽度 = (1 ÷ 4 × 338.16) + 92.09 = 176.63px
» 计算之后的Flex3项目宽度 = (1 ÷ 4 × 338.16) + 140.83 = 225.37px
» 计算之后的Flex4项目宽度 = (1 ÷ 4 × 338.16) + 189.56 = 274.1px

注意,不同的浏览器对小数处理有差异

flex-shrink

flex-shrinkflex-grow有点类似,不同的是 flex-shrink是用来控制Flex项目的收缩因子。通过前面的内容我们知道,在Flexbox布局中,有的时候Flex项目宽度总和会超过Flex容器宽度,造成Flex项目溢出Flex容器。对于开发者而言,总是希望Flex项目能根据容器的空间来自动调整自身的大小。针对这种场景,flex-shrink就能起作用了。

简单地说,flex-shrink会将Flex容器不足的空间按收缩因子的比例分配到Flex项目上,这个时候Flex项目就会根据比例收缩宽度,以免Flex项目溢出Flex容器。

flex-shrink语法规则也很简单:

flex-shrink: <number>

flex-shrink只接收正数值(可以是正整数,也可以是正小数),其默认值是1。即 Flex容器没有足够空间容纳所有Flex项目时,Flex项目默认都会收缩。如果你不想让Flex项目进行收缩,可以显式设置flex-shrink的值为 0,此时Flex项目始终会保持原始的fit-content宽度。

在Flexbox布局模块中,在使用flex-shrink时也可以按下面的方式使用:

  • 所有Flex项目设置相同的flex-shrink
  • 每个Flex项目设置不同的flex-shrink

正如上例所示,在Flex项目中不做任何设置,Flex项目会按相同的比例收缩(因为flex-shrink的默认值是1);显式设置flex-shrink0时,Flex项目不会收缩,将会溢出Flex容器;不同的Flex项目设置不同flex-shrink值时会按不同的比例收缩:

同样的,flex-shrink也有一套相应的计算公式:

flex-shrink计算公式看上去要比flex-grow复杂一些,过程也多一点,但只要你真正理解了,就不会再有这样的感觉了。同样用实例来向大家阐述,这样更易于理解flex-shrink是如何计算,以达到Flex项目根据收缩因子自动收缩(或者说分配Flex容器不足空间):

<!-- HTML -->
<div class="flex__container">
    <div class="flex__item" style="--flexShrink: 0">flex-shrink:0</div>
    <div class="flex__item" style="--flexShrink: 1">flex-shrink:1</div>
    <div class="flex__item" style="--flexShrink: 2">flex-shrink:2</div>
    <div class="flex__item" style="--flexShrink: 3">flex-shrink:3</div>
</div>

/* CSS */

:root {
    --flexShrink: 0;
}

.flex__container {
    inline-size: 40vw;
}

.flex__item {
    flex-shrink: var(--flexShrink);
    inline-size: 15vw;
}

效果如下:

从该示例的代码中我们可以获知:

  • Flex容器的宽度是 40vw (CSS中设置了inline-size: 40vw
  • Flex容器中共有四个Flex项目,并且每个Flex项目的宽度都是 15vw(CSS中设置了inline-size: 15vw
  • Flex项目宽度总和为 15vw x 4 = 60vw
  • Flex容器的不足空间为 40vw - 60vw = -20vw
  • Flex项目的flex-shrink的值分别是0123,所有Flex项目的flex-shrink总和为0 + 1 + 2 + 3 = 6

即:

flex-shrink公式中变量名称 Flex项目1 Flex项目2 Flex项目3 Flex项目4 总数
Flex项目的flex-shrink 0 1 2 3 0 + 1 + 2 + 3 = 6
Flex项目的宽度(width) 15vw 15vw 15vw 15vw 15vw x 4 = 60vw
Flex容器的宽度(width)         40vw
Flex容器的剩余空间         40vw - 60vw = -20vw
Flex项目新宽度(width) ? ? ? ?  

有了这些参数值之后,根据上面提供的flex-shrink计算公式就可以计算 总收缩宽度Flex项目收缩比例。 我们先来计算出总收缩宽度:

» 总收缩宽度 = ∑(Flex项目初始宽度 × Flex项目的flex-shrink值)
» 总收缩宽度 = (15vw × 0) + (15vw × 1) + (15vw × 2) + (15vw × 3) = 15vw × 6 = 90vw

有了总收缩宽度就可以计算出 Flex项目的收缩比例

» Flex项目的收缩比例 = (Flex项目的初始宽度 × Flex项目的flex-shrink值) ÷ Flex项目总收缩宽度

» Flex项目1的收缩比例 = (15vw × 0) ÷ 90vw = 0
» Flex项目2的收缩比例 = (15vw × 1) ÷ 90vw = 0.1667
» Flex项目3的收缩比例 = (15vw × 2) ÷ 90vw = 0.3333
» Flex项目4的收缩比例 = (15vw × 3) ÷ 90vw = 0.5

这样就可以计算出Flex项目的新宽度:

» Flex项目新宽度 = Flex项目初始宽度 - (Flex容器不足空间 × Flex项目收缩比例)

» Flex项目1新宽度 = 15vw - (20vw × 0) = 15vw
» Flex项目2新宽度 = 15vw - (20vw × 0.1667) = 11.666vw
» Flex项目3新宽度 = 15vw - (20vw × 0.3333) = 8.334vw
» Flex项目4新宽度 = 15vw - (20vw × 0.5) = 5vw

具体如下图所示:

其实flex-shrink还有另一套相对来说更为简单的计算公式:

根据上图的公式,计算出来的结果和前面计算的结果基本相等:

» Flex项目的新宽度 = Flex项目初始宽度 - (Flex容器不足空间 ÷ 所有Flex项目flex-shrink和) × Flex项目的flex-shrink

» Flex项目1的新宽度 = 15vw - (20vw ÷ 6) × 0 = 15vw
» Flex项目2的新宽度 = 15vw - (20vw ÷ 6) × 1 = 11.667vw
» Flex项目3的新宽度 = 15vw - (20vw ÷ 6) × 2 = 8.333vw
» Flex项目4的新宽度 = 15vw - (20vw ÷ 6) × 3 = 5vw

同样的,flex-shrink也可以取小数值,而且和flex-grow相似,如果所有Flex项目的flex-shrink总和小于1时,Flex容器的不足空间不会全部分完,Flex项目依旧会溢出Flex容器,比如下面这个示例:

另外,在Flexbox布局当中,会阻止Flex项目宽度缩小至0。如果某个Flex项目按照flex-shrink计算出来的新宽度趋向于0时,Flex项目将会按照该元素的min-content的大小来设置宽度,同时这个宽度将会转嫁到其他Flex项目,再按相应的收缩因子进行收缩。

在这个示例中,第四个Flex项目的flex-shrink的值为9。根据上面提供的公式,可以获知:

» Flex项目的新宽度 = Flex项目初始宽度 - (Flex容器不足空间 ÷ 所有Flex项目flex-shrink和) × Flex项目的flex-shrink

» Flex项目4的新宽度 = 15vw - (20vw ÷ 12) × 9 = 0

计算出来的宽度为0,但实际上这个时候渲染出来的宽度是该项目的min-content(该示例就是“shrink”单词的宽度),大约47.95px(约3.66vw)。那么这个值将会分成3份(因为该例另外三个Flex项目的flex-shrink012),并且对应的Flex项目会继续分配本应Flex项目4要收缩的宽度。即:

» Flex项目1新宽度 = 15vw - 20 ÷ 12 × 0 - 3.66 ÷ 3 × 0 = 15vw (约196.5px)
» Flex项目2新宽度 = 15vw - 20 ÷ 12 × 1 - 3.66 ÷ 3 × 1 = 12.113vw (约158.6847px)
» Flex项目3新宽度 = 15vw - 20 ÷ 12 × 2 - 3.66 ÷ 3 × 2 = 9.227vw (约120.869px)

浏览器视窗宽度在1310px状态下渲染出来的结果如下:

在Flexbox项目中,虽然flex-shrink会让Flex项目根据收缩因子缩小自身宽度,但是Flex项目自身显式设置了min-widthmin-inline-size时,计算出来的值要是小于min-width(或min-inline-size),Flex项目将会按min-widthmin-inline-size的值渲染:

这个时候,设置了min-width(或min-inline-size)的Flex项目会类似于上例中的min-content一样,将未分配的Flex容器不足空间转嫁到其他的Flex项目上。

相对而言,flex-shrink的计算要比flex-grow复杂一些,特别是当Flex项目根据flex-shrink计算出来的宽度趋于0时,会让计算变得复杂化

在Flexbox布局模块中,基于前面提到的Flex容器的对齐属性、Flex项目中的flex-shrinkflex-grow我们就可以很好的处理Flex容器的剩余空间和不足空间:

  • Flex容器有剩余空间(所有Flex项目的宽度总和小于Flex容器的宽度),如果设置了flex-grow,Flex项目会根据扩展因子分配Flex容器剩余空间;在未设置flex-grow时,在Flex容器中是否设置了对齐方式,如果是,那么会按对齐方式分配Flex容器剩余空间,如果不是,Flex容器剩余空间不变
  • Flex容器有不足空间(所有Flex项目的宽度总和大于Flex容器的宽度),如果设置了flex-shrink值为0,Flex项目不会收缩,Flex项目溢出Flex容器;如果未显式设置flex-shrink值,Flex项目分平均分配Flex容器不足空间,Flex项目会变窄(Flex项目的flex-shrink的默认值为1),如果显式设置了flex-shrink的值为非0的不同值,那么Flex项目会按照不同的收缩因子分配Flex容器不足空间,Flex项目同样会变窄

具体的我们可以绘制一张这方面的流程图:

flex-basis

在Flexbox布局模块中,除了我们熟悉的widthmin-widthmax-width以及逻辑属性inline-sizemin-inline-sizemax-inline-size可以用来设置Flex项目的初始宽度之外,还可以使用flex-basis属性,即 在任何Flex容器空间(剩余空间或不足空间)分配发生之前初始化Flex项目尺寸。其语法很简单:

flex-basis: content | <width>

其默认值为auto。其中content是指Flex项目内容的自动尺寸,而<width>是我们熟悉的带有数值和单位的值,比如10vw50%200px等;除此之外,还可以是一些用来描述宽度的关键词,比如fillmax-contentmin-contentfit-content()inheritinitialunset等。

如果flex-basis的值设置为auto,浏览器将先检查Flex项目的主尺寸是否设置了绝对值,再计算Flex项目的初始值。比如说,你给Flex项目显式设置了width: 200px,那么200px就是Flex项目的flex-basis值。

如果你的Flex项目可以自动调整大小,则auto会解析为其内容的大小,这个时候min-contentmax-content会起作用,并且Flex项目的max-content作为flex-basis的值。

相比flex-growflex-shrink而言,flex-basis是最复杂的部分。事实上,在Flexbox布局模块中 设置Flex项目的尺寸大小存在一个隐式的公式

contentwidthflex-basis

简单地说,如果Flex项目未显式指定flex-basis的值,那么flex-basis将回退到width(或inline-size)属性;如果未显式指定width(或inline-size)属性的值,那么flex-basis将回退到基于Flex项目内容计算宽度。不过,决定Flex项目尺寸大小,还受flex-growflex-shrink以及Flex容器大小的影响。而且Flex项目 最终尺寸 会受min-widthmax-width(或min-inline-sizemax-inline-size)属性限制。这一点必须得注意。

接下来用一个简单的示例来阐述contentwidth以及flex-basis对Flex项目尺寸的影响。

其中width是用来描述元素宽度的物理属性,从《图解CSS:CSS逻辑属性》一文中可以得知,与其相匹配的还有inline-sizeblock-size逻辑属性。注意,CSS逻辑属性受directionwriting-mode的值的影响。

示例代码很简单:

<!-- HTML -->
<div class="flex__container">
    <div class="flex__item"></div>
    <div class="flex__item"></div>
    <div class="flex__item"></div>
    <div class="flex__item"></div>
</div>

/* CSS */

.flex__container {
    width: 600px;
    display: flex;

    border: 1px dashed #f36;
    align-items: stretch;
}

按照上面的公式节奏来走,慢慢的加戏。

Flex项目不显式的设置任何与尺寸大小有关系属性,即用content来撑开Flex项目

在上面的HTML基础上添加内容:

<!-- HTML -->
<div class="flex__container">
    <div class="flex__item">Lorem ipsum dolor sit amet</div>
    <div class="flex__item">Lorem ipsum dolor sit amet consectetur adipisicing elit</div>
    <div class="flex__item">Fugiat dolor nihil saepe. Nobis nihil minus similique hic quas mollitia.</div>
    <div class="flex__item">Lorem ipsum dolor sit amet consectetur adipisicing elit. Molestias consequuntur sequi suscipit iure fuga ea!</div>
</div>

这个时候你看到的效果如下,而且每个Flex项目的宽度(以width为例)都依赖于元素内容的多少来控制:

注意,文本相关的CSS属性,比如font-sizefont-family等属性此时会影响Flex项目尺寸的大小

在这个示例中,并没有显式给Flex项目设置flex-basis属性,此时flex-basis会取默认值auto

正如上图所示,浏览器将flex-basis属性的值以auto计算。此时 flex-basis的值会采用Flex项目内容计算出宽度

  • 第一个Flex项目计算出来的宽度值为62.61px
  • 第二个Flex项目计算出来的宽度值为126.55px
  • 第三个Flex项目计算出来的宽度值为161.06px
  • 第四个Flex项目计算出来的宽度值为247.78px

接下来看第二步:

显式给Flex项目设置width

给所有Flex项目设置width: 120px

:root {
    --width: 120px;
}

.flex__item {
    width: var(--width);
}

效果如下:

这个时候所有Flex项目宽度都是相等的:

这个示例中,只是显式给所有Flex项目设置了width: 120px。浏览器计算出来的flex-basis值依旧为auto,但显式的设置了width: 120px最终width属性的值决定了Flex项目的尺寸大小

继续加码:

显式给Flex项目设置flex-basis值,即Flex项目同时有widthflex-basis

为了更好的区分flex-basiswidth,在示例中把flex-basiswidth设置不同的值:

:root {
    --width: 120px;
    --flexBasis: 150px;
}

.flex__container {
    width: 800px;
}

.flex__item {
    width: var(--width);
    flex-basis: var(--flexBasis);
}

效果如下:

在这个示例中,虽然在Flex项目同时显式设置了widthflex-basis,但Flex项目最终的尺寸大小采用了flex-basis的值:

也就是说,同时显式给Flex项目设置flex-basiswidth的值,则width会被忽略,不再起作用,相当于没有设置

在该示例中,所有Flex项目宽度的总和是 600px,小于Flex容器宽度 800px,Flex容器的剩余宽度为200px:

如果所有Flex项目宽度的总和大于Flex容器宽度会怎么样呢?我想你可能知道结果了,但这里还是花点时间再向大家印证一下。我们在上例的基础上将Flex项目的flex-basis值调整为300px

:root {
    --width: 120px;
    --flexBasis: 300px;
}

效果如下:

当你没看到上面的渲染效果时,你是否会认为,Flex项目应该是取值flex-basis,即300px。Flex项目会溢出Flex容器:

如果你真的是这么的认为,那说明你没有仔细阅读前面有关于flex-shrink的内容。事实上,Flex项目的宽度并不是width的值150px,也不是flex-basis的值300px,而是一个被重新计算后的值 199.5px

这就是Flexbox布局模块的强大之处,会有这样的结果都归功于flex-shrink属性。从前面的内容我们可以得知,当Flex容器有不足空间(该示例为200px),并且未显式设置flex-shrink属性,那么浏览器将会取其默认值1作为收缩因子,并且根据flex-shrink计算公式计算出每一个Flex项目应该在原有宽度300px上减去多少应该收缩的宽度:

» Flex项目的新宽度 = Flex项目初始宽度 - (Flex容器不足空间 ÷ 所有Flex项目flex-shrink和) × Flex项目的flex-shrink

» Flex项目的新宽度 = 300px - (402px ÷ 4) × 1 = 199.5px

注意,在该示例中,Flex容器设置了1px的边框,计算出来Flex容器不足的空间为402px

前面提到过,在Flexbox布局模块中影响Flex项目尺寸大小应该根据其隐式公式(即 contentwidthflex-basis )来进行判断。如果要显式给Flex项目设置尺寸大小,其最佳方式是 使用flex-basis,而不是width(或inline-size)

最后还有一点千万别忘记:

使用flex-basis时会受min-widthmax-width(或逻辑属性中min-inline-sizemax-inline-size)的限制

在详细阐述这一点之前,我们先来回忆一下。

在CSS中除了通过width(或height)显式给一个盒子定义尺寸之外,还可以通过min-width(或min-height)和max-width(或max-height)来定义盒子尺寸。如果拿widthmin-widthmax-width同时给一个盒子设置宽度时,具体取值为哪个?这是一个很有意思的问题。我们可以通过这三者之间的权重来取决:

  • 元素的width大于max-width时,元素的width等于max-width,即 max-width能覆盖width
  • 元素的width小于min-width时,元素的width等于min-width,即 min-width能覆盖width
  • min-width大于max-width时,min-width优先级将高于max-width

这个规则同样适用于inline-sizemin-inline-sizemax-inline-size

同样的,当flex-basismin-widthmax-width同时出现时,也可以套用上面的规则,最终客户端会决定哪个值运用于Flex项目上。这就是为什么说 flex-basis会受制于min-widthmax-width。 事实上,我们在介绍flex-growflex-shrink时也提到过,当flex-grow计算出来的值大于max-width值时,会取max-width的值;当flex-shrink计算出来的值小于min-width值时,会取min-width值。

如果Flex项目同时出现widthflex-basismin-width时,具体的运算过程如下:

  • 根据法则: contentwidthflex-basis,判断出运用于Flex项目的值,即flex-basis会运用于Flex项目
  • 再根据法则:Flex项目的 width 小于 min-width时,Flex项目的 width 等于 min-width,即 min-width能覆盖width

这样一来,如果flex-basis小于min-width时,Flex项目的宽度会取值min-width,即min-width覆盖flex-basis

如果Flex项目同时出现widthflex-basismax-width时,具体的运算过程如下:

  • 根据法则: contentwidthflex-basis,判断出运用于Flex项目的值,即flex-basis会运用于Flex项目
  • 再根据法则:Flex项目的 width 大于 max-width时,Flex项目的 width 等于 max-width,即 max-width能覆盖width

这样一来,如果flex-basis大于max-width时,Flex项目的宽度会取值max-width,即max-width覆盖flex-basis

如果Flex项目同时出现widthflex-basismin-widthmax-width时,会在上面的规则上增加新的一条规则来进行判断:

min-width 大于 max-width 时,min-width优先级将高于max-width

那么套用到Flex项目中:

  • flex-basis大于max-width,Flex项目的宽度等于max-width,即 max-width能覆盖flex-basis
  • flex-basis小于min-width时,Flex项目的宽度会取值min-width,即 min-width覆盖flex-basis

由于min-width大于max-width时会取min-width,有了这个先取条件我们就可以将flex-basismin-width做权重比较,即:flex-basis会取min-width。反过来,如果min-width小于max-width时则依旧会取max-width,同时要是flex-basis大于max-width就会取max-width

如果你理解了的话,可以使用更简单的规则来决定用于Flex项目的尺寸。

首先根据contentwidthflex-basis来决定用哪个来决定用于Flex项目。如果Flex项目显式设置了flex-basis属性,则会忽略contentwidth。而且min-width是用来设置Flex项目的下限值;max-width是用来设置Flex项目的上限值

虽然使用flex-basis显式设置Flex项目的尺寸优于widthheight,但这仅是理想尺寸。因为在Flexbox布局中就算是你显式设置了flex-basis也不能保证这将是Flex项目的最终尺寸。那是 Flex项目放入Flex容器之后一切都将有可能改变。前面也提到过了,Flex项目的实际尺寸大小会被flex的另外两个属性flex-growflex-shrink的影响。简单地说,如果容器有足够多的空间,Flex项目会根据扩展因子扩展到足够填充Flex容器;反之,如果容器没有足够多的空间容纳Flex项目时,Flex项目会根据收缩因子缩小到足够被容纳在Flex容器中

怎么设置Flex项目的基本大小

通过前面的学习,我们可以获知,对于Flex项目,显式设置flex-basis是一个最佳方案。但对于如何设置Flex项目的基本大小,我们可以围绕以下几点来进行思考:

  • flex-basis的值是auto?Flex项目显式的设置了宽度吗?如果设置了,Flex项目的大小将会基于设置的宽度
  • flex-basis的值是auto还是content?如果是auto,Flex项目的大小为原始大小
  • flex-basis的值是0的长度单位吗?如果是这样那这就是Flex项目的大小
  • flex-basis的值是0呢? 如果是这样,则Flex项目的大小不在Flex容器空间分配计算的考虑之内

如果用简单的话来概括的话:

  • flex-basis可以判断出Flex容器是存在剩余空间,还是存在不足空间
  • 如果Flex容器存在剩余空间,flex-grow可以按扩展因子来扩展Flex项目大小
  • 如果Flex容器存在不足空间,flex-shrink可以按收缩因子缩小Flex项目大小
  • 如果Flex项目显式设置了max-width值,即使Flex项目按照flex-grow扩展因子扩展了Flex项目尺寸,也不会大于max-width的值
  • 如果Flex项目显式设置了min-width值,即使Flex项目按照flex-shrink收缩因子收缩了Flex项目尺寸,也不会小于min-width的值

第三部分正在努力更新中,敬请期待...