[CSS] Flex/Grid Layout Modules, part 7

我们继续来深入关於 Grid 容器的相关样式设定。虽然目前 CSS 框架在多数情况下并不需要特别在意,但,就老话一句,谁在意谁痛苦。

踩到雷而且你还不知道为什麽


新的单位 fr

从 Grid 出现之後,我们有一个新的尺寸单位可以使用,他叫做弹性长度(flexible length),在 CSS 当中,因应这个弹性长度,所以就有了新单位 fr,而 fr 的全名为 弹性轨道flexible tracks),或者你会听到有人称他为 弹性系数(flexible factor

这个单位的定义与使用状况在这边稍微给大家解释一下,

  1. 分配 剩余空间
  2. 计算方式 <fr 系数> * <剩余空间> / <所有 fr 系数总和>
  3. fr 会因为内容计算而产生不同宽度
  4. minmax() 可以用於计算 fr
  5. calc() 不能使用 fr 与其他单位数值混合运算
  6. fr 在介於 01 之间的小数时有其特殊计算方式

首先,我们来定义一下何谓 剩余空间

https://ithelp.ithome.com.tw/upload/images/20210911/20001433v6mxteJXUW.png

好的,所以我们的容器如果是一个固定尺寸的空间,我们举个简单的数字来方便大家里解,

.grid-container {
    display: grid;
    grid-template-rows: repeat(4, 1fr);
    grid-template-columns: repeat(4, 1fr);
    
    width: 1000px;
    height: 1000px;
}

这样我们会获得一个 1000x1000 尺寸的容器,然後里面有 4x4 总共 16 个 Grid 单元空间。由於上述的公式,我们经过简单的四则运算,

1 * 1000px / 4 = 250px

所以我们可以很明确的知道,我们所画出来的 Grid 单元空间有 250px 宽,然後 250px 高。在这个地方很容易理解,但,我们暂时 不考虑 Grid 单元内容会造成的影响。这个在後面提到尺寸的时候会再详细描述。

另外,关於介於 01 之间的 fr 设定,由於这边解释起来相当绕口,所以我先举几个例子,然後大家先看看图,後面我再来解释到底发生了什麽事情。

首先,我们一样使用固定尺寸空间,然後稍微换一下里面的东西,

.grid-container {
    display: grid;
    grid-template-rows: repeat(3, 1fr) 0.5fr;
    grid-template-columns: repeat(3, 1fr) 0.5fr;
    
    width: 1000px;
    height: 1000px;
}

这样,由於分配的 剩余空间 还是使用 1000px 整份去切割,所以基本上这个还是符合我们刚刚所说的剩余空间分配公式,

关於 1fr 的部分是:
1 * 1000px / 3.5 = 285.714px

关於 0.5fr 的部分是:
0.5 * 1000px / 3.5 = 142.857px

接着,我们来看看搭配了非弹性空间单位的情况,

.grid-container {
    display: grid;
    grid-template-rows: repeat(3, 150px) 0.5fr;
    grid-template-columns: repeat(3, 150px) 0.5fr;
    
    width: 500px;
    height: 500px;
}

https://ithelp.ithome.com.tw/upload/images/20210911/20001433maPjJ1FK7q.png

根据上述的公式,我们可以简单计算 剩余空间 如下,

500px - 150px * 3 = 50px

所以这边我们还可以预期的到,我们的 0.5fr 是使用剩余空间来分配,

0.5 * 50 = 25px

接着,当我们出现了第二个小数点的 fr 单位时,

.grid-container {
    display: grid;
    grid-template-rows: repeat(2, 150px) 0.5fr 0.5fr;
    grid-template-columns: repeat(2, 150px) 0.5fr 0.5fr;
    
    width: 500px;
    height: 500px;
}

https://ithelp.ithome.com.tw/upload/images/20210911/20001433CKAltJ0hiu.png

当我们天真的以为,两个 0.5fr 就会帮我把剩余空间各半分配。但是,实际上并不会这样运作,你会看到 Grid 容器帮你把剩余空间全部分配完毕了。这边的计算方式很类似 Flexbox 的 flex-grow 在介於 01 的计算方法,但稍微有点不同。

他的运作方式是,

fr 介於 01 之间时,轨道尺寸计算会使用非 100% 的剩余空间做计算。其计算的方式,是先假定内容最大尺寸(max-content)来当作弹性系数,此时会产生一个假想的 1fr 宽度来做运算,最後再将空间分配给所设定的弹性系数,最後会得到一个 最终分配系数

如果把他想成数学的话会比较简单,

<剩余空间> / <fr 单位数量> * <fr 系数> = <最终分配 fr 系数>

所以我们刚刚的设定最後会得到什麽 Grid 单位空间呢?

200px / 2 * .5 = 1fr

所以,最後剩余空间会变成两个 1fr 去分配。而不是你所想像的,有两个 Grid 单位空间,然後各占一半的事情发生。

为什麽?

fr 尺寸这样的设计,是为了在无指定容器尺寸的情况下,避免分配状况出现问题,进而搭配 max-content 与假想 1frhypothetical 1fr size)的方式来均分剩余空间。重点在於确保弹性尺寸在 Grid 轨道上有确切尺寸,且能还能依照所设定的比例去分配空间。

再来一个比较讨厌的例子,

.grid-container {
    display: grid;
    grid-template-rows: repeat(2, 150px) 0.25fr 0.5fr;
    
    width: 100%;
}

https://ithelp.ithome.com.tw/upload/images/20210911/20001433hoFmsP6G1M.png

你可能会以为他跟刚刚一样会把空间填满?并不会喔!你会获得一个 没有分配的剩余空间,然後基本上他虽然属於 Grid 容器,但他 永远不会被使用到

因为所有介於 01 之间的弹性系数设定,都仅会拿剩余空间来做重新分配,且不会填满(也就是非 100%)剩余空间的计算方式,来重新分配,所以出现没有分配的剩余空间是很有可能发生的事情。

所以,当你在使用 fr 的时候,如果不确定你在干嘛,请不要使用介於 01 之间的设定。

最後,来提及内文影响 Grid 单元的部分,假设我们设定了一个很简单的 Grid 容器,

.grid-container {
    display: grid;
    grid-template-rows: repeat(4, 1fr);
}

我们有一个很美好的 Grid 容器,然後我们请一只猫来输入内容,

<div class="grid-container">
    <div class="grid-item">
        我的第一个 Grid 内文
    </div>
    
    <div class="grid-item">
        我的第一个 Grid 内文
    </div>

    <div class="grid-item">
        我的第一个 GGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGrid 内文
    </div>

    <div class="grid-item">
        我的第一个 Grid 内文
    </div>
</div>

https://ithelp.ithome.com.tw/upload/images/20210911/20001433YV079PUwak.png

说好的 1fr 会均分呢?

谁跟你说好,格线系统并没有跟你说一定会均分。

这个问题起源在这里,

[css-grid] Reconsider the meaning of 1fr #1777

翻成白话的意思就是,对於 1fr 实际上的预设定义是 minmax(auto, 1fr),所以这就很显而易见,当那个 auto 生效的时候,就会取 max-content 来当作一个尺寸。这也就是为何你的 1fr 不会均分的原因。

所以,後来就出现了 minmax(0, 1fr) 的写法,我不知道这算不算一种 Workaround,但是,Workaround 虽然可耻,但是相当有用

.grid-container {
    display: grid;
    grid-template-rows: repeat(4, minmax(0, 1fr));
}

https://ithelp.ithome.com.tw/upload/images/20210911/20001433wgJbJvTygd.png

请注意,当你使用 minmax(0, 1fr) 的时候,你的内文会被切割,请自行使用换行相关的样式,将你的内文做适当的换行。当然,这个问题会发生在两个轴方向上,换句话说,当你在列(row)相关的地方使用 fr 均分时,也会发生一样的问题。

但,由於我们的文件流向大部分是由上到下,所以比较少特别限制 高度 的部分,但,这还是取决於你的使用目的,只是在此提醒一下两个轴方向皆会发生上述的所有事情。


容器对齐与定位

上一篇没有特别提及的对齐,这边会连同定位再次让大家里解一下关於 Grid 容器对齐的事情。首先关於对齐,我们可以分成两个面向来看,

  1. 所有 Grid 单元集合
  2. 每一个 Grid 单元
  3. 仅单一 Grid 单元
样式 适用对象
justify-content, align-content, palce-content 所有 Grid 单元集合
justify-items, align-items, place-items 每一个 Grid 单元
align-self, justify-self, place-content 仅单一 Grid 单元

https://ithelp.ithome.com.tw/upload/images/20210911/200014333V6tV3sfgD.png

上图中绿色的框线代表了 所有 Grid 单元集合,换句话说,当你的容器有剩余空间时,你所设定的 justify-contentalign-content 才会发生效果。这个情况在 Grid 单元所使用的 justify-selfalign-self 也是一样的道理。

也就是说,当你的 Grid 单元尺寸设定,相较於整个 Grid 容器所规划的尺寸,有产生 剩余空间 时,才会发生效果。

另外,这边必须要特别提及可以使用 stretch 的系列样式,包含了,

  • justify-items
  • justify-self
  • align-items
  • align-self

当你的 Grid 单元并无指定特定尺寸时(或有产生剩余空间时),这个 stretch 才会生效,请特别留意。

另外,在 Grid 容器整个系统内,有一个特别的设计,这个部分跟定位有关。在前几天我们聊到 Flexbox 对於定位会打破 Flex 流向的状况比较不同,具体上可以分为这两种,

  1. 指定网格单元容器(Container block)的绝对定位元件
  2. 直接相对於 Grid 容器的绝对定位元件

第二点的部分跟 Flex 雷同,他会跳脱整个 Grid 生态系,但是,在不指定位置的情况下,还是会跟着整个生态系的设定走,举例来说,

.grid-container {
    display: grid;
    grid-template-rows: repeat(3, 1fr);
    grid-template-columns: repeat(3, 1fr);
    
    position: relative;
}

.grid-item:first-child {
    position: absolute;
}

https://ithelp.ithome.com.tw/upload/images/20210911/20001433DISe3XoJit.png

而,如果你的 Grid 单元也有相关设定,如 justify-self, align-self 时,在没有指定定位点的情况下,也是会跟着 Grid 生态系的设定呈现,

https://ithelp.ithome.com.tw/upload/images/20210911/20001433fPYMrEL0Rj.png

接着是比较特别的网格单元容器,当你的网格单元有指定一个区域时,这个定位的效果就会发生一些变化。何谓 指定一个区域 呢?我们举个简单的例子来看,

.grid-container {
    display: grid;
    grid-template-rows: repeat(3, 1fr);
    grid-template-columns: repeat(3, 1fr);
    
    position: relative;
}

.grid-item:first-child {
    grid-row: 2 / 4;
    grid-column: 2 / 4;
    
    position: absolute;
    top: 50px;
    left: 50px;
}

在这个时候,我们将 Grid 单元指定了所使用的 Grid 容器内的范围,如果还不熟悉样式写法的人先不用紧张,我在此简单解释一下,

  • 指定列的范围从网格格线 2 开始,直到网格格线 4 结束
  • 指定栏的范围从网格格线 2 开始,直到网格格线 4 结束

接着我们定义了定位点的上边与左边,各设定了 50px 的距离,最终,我们会得到这样的结果,

https://ithelp.ithome.com.tw/upload/images/20210911/20001433YzK1AhmoZI.png

上图的红色框线部分,就是我们所指定的区块范围,

grid-row: 2 / 4;
grid-column: 2 / 4;    

这一点是比较特别的,当你在 Grid 单元中使用绝对定位(absolute)时,他会依照不同的情况而发生不一样的效果。

position: fixed 无此效果,请注意!


小结

关於尺寸与定位今天就先聊到这边,明天我们继续来聊 Grid 生态系中的格线系统。


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


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


<<:  AI ninja project [day 12] 图片分类(2)

>>:  sklearn dataset make_moons() make_circles()

Day28 - [Shioaji] 超入门!永丰证券程序交易API快速上手 (1)

一晃眼,铁人赛就进入了尾声,先前一直说有时间要来写Shioaji的,总是不能食言。我想就用最後两篇的...

完赛心得 & Web Exploit 通关心得

前面部份题目未解,仍努力中... 这次挑战的初衷是想看看能不能藉由游戏化的方式让资安学习简单一点,老...

Ruby 学习笔记簿:Metaprogramming Workshop - Before Action

实作前准备 需要先了解以下主题: Method Wrappers: Around Aliases M...

Material UI in React [Day 6] Theme (Globals) & Inputs (Buttons)

Globals 这里先提一下 key: overrides,当组件间相互传递的 key: props...

[Day21] 与问题成员对话-案例三:PIP

对话,有很高的机会,可以让主管与团队成员间,重新对齐目标,提升团队效能外,偶尔也会有例外状况。根据经...