[CSS] Flex/Grid Layout Modules, part 5

数学不会背叛你,数学不会就是不会。

我现在写三角函数都是去 Google 的,不要问。注意!本篇可能会出现大量的加减乘除,如有出现头晕目眩、恶心想吐、手脚冰冷无力等状况,请立即关闭本篇文章,闪光洽关心您的身体健康。

或者你要去验孕也是可以的。


容器计算

前几篇有提到剩余空间、元件填充等等,我们现在就来看一下实际上 Flex 容器是怎麽做运算的。首先是 flex-grow 的计算方式,在数值总和大於 1 的一般情况下来看,但且记得,必须容器上有存在 剩余空间 的情况下才能被分配。

https://ithelp.ithome.com.tw/upload/images/20210907/20001433x0lmTJaEWH.png

所以我们来描述一下我们的容器跟元件,

  1. Flex 容器为 600px 主要轴尺寸。
  2. Flex 元件 1 为宽度 200pxflex-grow 设定为 1
  3. Flex 元件 2 为宽度 100pxflex-grow 设定为 2

首先,我们可以得知剩余空间为,

剩余空间 = 600px - (200px + 100px) = 300px

然後我们就能依照 flex-grow 来分配,

Flex 元件 1 扩充宽度 = 300px x 1 / (1 + 2) = 100px
Flex 元件 2 扩充宽度 = 300px x 2 / (1 + 2) = 200px

所以画面最後会得到,

Flex 元件 1 宽度 = 200px + 100px = 300px
Flex 元件 2 宽度 = 100px + 200px = 300px

看起来是不是没有很难,只要会加减乘除就好了。然後,我在一开始有提到 flex-grow 总和小於 1 的情况,

  1. Flex 容器为 600px 主要轴尺寸。
  2. Flex 元件 1 为宽度 200pxflex-grow 设定为 0.1
  3. Flex 元件 2 为宽度 100pxflex-grow 设定为 0.2

剩余空间就不赘述了,但是分配的方式不太一样。

Flex 元件 1 扩充宽度 = 300px x .1 / 1 = 30px
Flex 元件 2 扩充宽度 = 300px x .2 / 1 = 60px

所以你会发现最终我们得到的总宽度是,

Flex 元件 1 宽度 = 200px + 30px = 230px
Flex 元件 2 宽度 = 100px + 60px = 160px

这也是为何容器虽然有 flex-grow 但是没有被填满的情况。至於 flex-shrink 的部分基本上也是雷同的,只是从扩充变成压缩方向相反了而已。


尺寸干扰

对於 flex-grow, flex-shrink 这两件事情,被干扰的机率其实颇高,

  1. min-width, max-width
  2. min-content, max-content
  3. max(), min()

如果不确定自己在做什麽,尽量不要在使用 flex-growflex-shrink 的情况下,使用这些设定、函数或关键字,还要预期会得到相对应的尺寸。

基本上在弹性设计结构的状况下,Flex 元件尺寸应该是提供一种 确保 空间使用的状况符合预期,请尽量不要把 Flex 元件尺寸当作是期待值。如果你要这麽做,请确保你使用的是 flex: 0 0 auto,并且保证你的元件都有相对应尺寸。

至於 minmax(),在 w3c 有特别说明 minmax() 这个 CSS Function 是给 Grid Layout 使用,所以你在 Flexbox 元件上并无法使用这个函示来计算尺寸。

回到一开始我们提到的 flex-basis 的相关描述,我们再来补充一点看起来比较 没用 少用的写法,这里的重点,

请留意 width 何处会失效

设定 宽度有效值
flex-basis: max(10vw, 50rem); 计算数值,取当下最大值
flex-basis: max(10vw, 50rem); width: 60px 计算数值,取当下最大值
flex-basis: min(10vw, 50rem); width: 60px 计算数值,取当下最小值
flex-basis: min(10vw, 50rem); width: content 计算数值,取当下最小值
flex-basis: auto; width: min(10vw, 50rem); 计算数值,取当下最小值
flex-basis: auto; width: content; 计算数值,取容器内容尺寸
flex-basis: min(10vw, 50rem); max-width: 80px; 80px
flex-basis: min(10vw, 50rem); min-width: 100rem; 100rem

总结来说,除了 max-width, min-width 会强迫覆写 flex-basis 以外,扣除 auto 会被 width 覆写,其余的设定都还是以 flex-basis 为准。换句话说,如果你使用以下的写法,就会遇到一些很神奇的事情,

.flex-container {
    width: 600px;
}

.flex-item {
    flex: 0 0 content;
    width: 200px;
}

如果我们所使用的 HTML 结构像是这样,

<div class="flex-container">
    <div class="flex-item">1</div>
    <div class="flex-item">2</div>
    <div class="flex-item">3</div>
    <div class="flex-item">4</div>
</div>

根据简单的数学,我们有 4 个容器元件,每个容器元件定义了 width: 200px,所以我们理论上会得到,4 x 200px = 800px 总共是 800px 的元件尺寸。然而,当我们的元件容器使用 flex: 0 0 content 时,我们的想像是,

  1. 不会填充
  2. 不会压缩
  3. 依照元件内容设定尺寸

还记得我们 第一篇 写了这个结果吗?

设定 宽度有效值
flex-basis: content; width: 60px; 60px

所以真的吗?我们来看看实际结果,

https://ithelp.ithome.com.tw/upload/images/20210907/20001433TtPhAfINJf.png

惊不惊喜?意不意外?

然而,当我们把他分开来写的时候,

.flex-item {
    flex-grow: 0;
    flex-shrink: 0;
    flex-basis: content;
    width: 200px;
}

在这个时候他会恢复正常,这才是我们觉得不被压缩的样子。

为什麽?

官方有这样的说明,

A unitless zero that is not already preceded by two flex factors must be interpreted as a flex factor. To avoid misinterpretation or invalid declarations, authors must specify a zero <‘flex-basis’> component with a unit or precede it by two flex factors.

因为 flex: 0 0 content 这样的设定,是两个 0 的无单位因子,再加上 content 这种无单位 flex-basis 设定,所以会造成混淆。他会被当作单一弹性因子来看待,也就是等同於只设定了 flex-growflex-basis 两组设定而已。

为何只有这两组?请看原始 flex 的设定值,

none | [ <‘flex-grow’> <‘flex-shrink’>? || <‘flex-basis’> ]

这样可以理解为何会出错了吧。由於 flex-shrink 预设为 1,所以当我们照刚刚的写法来做的时候,就会被转成 flex: 0 1 content 这样的结果,并不会是你所设定的 flex: 0 0 content後者的设定是不太合法 的写法。

不能说写错,而是在理解上官方的规定如此。

auto 不在此限。


零尺寸元件

另外一点,虽然你可以定义 Flex 元件尺寸,但不代表这个元件尺寸不会发生零尺寸(zero-sized)的情况。根据 Flex 容器的特性,这些所谓的零尺寸元件,会尽可能的被放在同一行,也就是说,在多行的情况下,即便第一行最後一个元件刚好填满容器,在下一行开始之前的零尺寸元件,都会被放在上一行里面。

其实零尺寸元件除了是空白元件外,也可能是宽度设定为 0 的元件。


关於 gap

CSS Box Alignment Module Level 3 当中,已经提供了 gap 的样式可以使用,目前的支援度来说也算不错,

https://ithelp.ithome.com.tw/upload/images/20210907/20001433pmLauewpsJ.png

前几篇提到了 gap 这件事情,有这个样式就能解决使用 paddingmargin 的宽度问题。

.flex-container {
    display: flex;
    gap: 10px;
}

请留意,gap 还是有分轴方向,所以会有以下几种写法,

  1. column-gap 交叉轴方向的间隔。
  2. row-gap 主要轴方向的间隔。
  3. gap 两个轴方向的间隔,等於上述两个缩写。

https://ithelp.ithome.com.tw/upload/images/20210907/20001433M1eQXz978k.png

但是,这不是没有後遗症的。

<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>

我们在这种结构下使用 gap 的设定,

.flex-container {
    display: flex;
    width: 800px;
    gap: 10px;
}

.flex-item {
    flex: 0 0 auto;
    width: 200px;
}

最终会造成什麽结果呢?

https://ithelp.ithome.com.tw/upload/images/20210907/200014339mh2UmMICf.png

请注意,如果你的元件 flex-shrink 设定为 0。那麽,gap 的空间就会将 Flex 容器撑开,也就是,当你的 Flex 容器使用 overflow: hidden 的话,最後一个 Flex 元件会被切断(因为超出了原本宽度设定了)。

而,若你将 flex-shrink 设定为非零值,那麽,你的 Flex 容器尺寸会生效,gap 的尺寸加上 Flex 元件尺寸,会视为 溢出尺寸 来处理,只是,gap 尺寸是 永远不会被压缩,所以倒楣的就是 Flex 元件。


小记

Flexbox 其实也没有很复杂,最麻烦的其实还是尺寸处理。剩下的就交给上天安排就可以了,如果发现不对劲可以掷茭问一下妈祖也是可以的。

Flexbox 告一段落,明天会开始讲 Grid 的部分。其实无论是 Flex 还是 Grid,多半都会牵扯到很多其他的模组,不过全部拉进来讲会过於离题,所以 Flex 的部分就到此为止。

Flexbox 小游戏,有兴趣的可以玩玩看。
https://flexboxfroggy.com


目录与小节:
[CSS] Flex/Grid Layout Modules, part 1


部落格同步放送:
[CSS] Flex/Grid Layout Modules, part 5


<<:  [Day 2] SRE - 你的服务死後不要让人担心嘛

>>:  IIS WordPress 永久连结如何移除 index.php 路径

Day 04:看看 Angular CLI 对我们做了什麽?认识专案架构

今天,我们要来看看在使用 Angular CLI 後,专案的架构会长什麽样? angular.jso...

[day-13] Python 内建的数值类函式

Python 内建的数值类函式 数值类函式 执行结果 功能 abs(-10) 10 取绝对值 min...

食谱搜寻系统制作_中

**制作目标 ** 资料库搜寻 在输入料理搜寻模式後,会让使用者输入料理名称或一项食材,再从资料库里...

[Day27] 建立购物车系统 - 10

本篇同步发文在个人Blog: 一袋.NET要扛几楼?打造容器化的ASP.NET Core网站!系列文...

【Day2】变数宣告var、let、const的区别

但是老师教我用var宣告变数,但我也看到有同学用 let 与 const 宣告变数,这是怎麽一回事...