CSS - 网格布局(grid)

川长思鸟来 2023-09-28 14:17 38阅读 0赞

目录

什么是网格布局

网格布局与弹性布局的比较

网格布局中的概念名词

网格容器 display:grid、display:inline-grid

网格轨道 grid-template-rows、grid-template-columns

min-content、max-content

minmax

repeat函数

auto-fill、auto-fit

网格单元

网格项目

网格线

网格线占位

网格区域占位

grid-template

显示网格、隐式网格

密集模式

网格间距

网格单元的对齐

网格项目的对齐


什么是网格布局

网格布局非常类似于表格布局,它们都是通过定义n行m列,来将容器划分为n*m个单元格。

下面是表格布局

091a5bb436f9430d91ddafc3c03d5d69.png

表格布局,是通过tr标签来定义行,通过td标签来定义每行的单元格(相当于定义列)

下面是网格布局

aa69f739f1da47a488886f2a15abc07d.png

网格布局,是为容器元素添加display:grid样式,来把容器元素变为网格容器,通过网格容器的grid-template-rows样式来定义各行高度,grid-template-columns样式来定义各列宽度。而网格容器的直接子级元素默认依次放入每个单元格中。

对比来看,表格实现行列依赖于标签结构,网格实现行列依赖于样式。

从页面渲染性能角度来看,肯定是网格的性能更佳,因为同样渲染一个单元格,网格中单元格的元素的层级更浅。一个完整表格单元格层级应该是table>tbody>tr>td,而网格单元格的层级是.container > .gird。

另外网格容器可以更好地支持单元格的布局,比如单元格的跨行跨列,比如单元格在网格中的位置。

网格布局与弹性布局的比较

我们再用弹性布局来实现上面的网格效果

a1277e06a3be480fb7157dac05bd0c4e.png

可以发现,弹性布局似乎更加简单地实现了网格效果 ,但是这种网格效果本质是一维的,而不是二维的。

CSS - 弹性布局_伏城之外的博客-CSDN博客

之前,我们学习弹性布局时,了解到弹性布局其实是一维布局,所谓一维布局,即弹性项目元素只会沿着弹性容器的主轴方向排列,虽然上面例子中,弹性项目看上去是二维的,但是实际上,第二行,第三行的弹性项目元素都是被挤下来的,如果我们不设置换行wrap,则这些弹性项目元素将全部在一行上排列。

d730b53cb8334fe897a473384d182f9b.png

所以,弹性布局只能让元素在一维轴线上实现很好的布局,而无法实现真正二维网格的效果,比如弹性布局中弹性项目就很难实现单元格跨行,如让下图中1,4,7单元格合并。

3e616a4d78234278abdcfce8553b0b93.png

因为弹性布局本质是一维布局,即1,4,7本质是一行上的,它们之间隔着2,3和5,6,所以无法轻松地实现合并。

e25b1318a2064f44bb538ebd4a7ea51d.png而网格布局是二维布局,它的单元格是由多条行线和列线分隔出来的,是真正二维意义上的网格效果。

我们可以很容易地依赖于网格线来完成单元格跨行跨列,如网格布局实现1,4,7合并

ce455522ba4b4ac59c615da2d25cf616.png

网格布局中的概念名词

  • 网格容器(gird container)
  • 网格轨道(grid track)
  • 网格单元(grid cell)
  • 网格项目(grid item)
  • 网格线(grid line)
  • 网格区域(grid area)
  • 网格间距(grid cell gap)
  • 显示网格(explicit grid)& 隐式网格(implicit grid)

网格容器 display:grid、display:inline-grid

当我们为一个元素添加如下样式之一

  • display:grid
  • display:inline-grid

该元素就变成了网格容器。

在标准流中:

  • display:grid 网格容器表现为块级元素特性;
  • display:inline-grid 网格容器表现为行内块元素特性;

dd22ab1da5844cc68c3370e0cd4b459f.png

整个网格系统都在网格容器的 content-box 中。

7f5ea465c8cd4d1c90a4f526d29a61e7.png

网格轨道 grid-template-rows、grid-template-columns

所谓网格轨道,即网格行和网格列的统称。

我们可以通过

  • grid-template-rows 来定义网格行的数目和尺寸
  • grid-template-columns 来定义网格列的数目和尺寸

5fb928c69a7f486a82f1c15e11cbc897.png

上例中:

  • grid-template-rows: 100px 100px 100px; // 表示设置了3行,每行尺寸(高度)为100px
  • grid-template-columns: 100px 100px 100px; // 表示设置了3列,每列尺寸(宽度)为100px

我们需要注意的是网格轨道并非真实的DOM元素,而是一种逻辑概念,所以它不会被浏览器渲染出来,上例中网格轨道的效果图是通过浏览器的grid布局检测模拟出来的。

grid-template-rows、grid-template-columns不仅支持固定尺寸,还支持百分比尺寸、fr尺寸、auto尺寸、min-content、max-content尺寸、minmax()尺寸

135374c9a54a4d5d8a42bfb0c55fccdd.png

这里百分比尺寸的基数是网格容器的尺寸。如网格行的百分比尺寸的基数,就是网格容器的content-box的height,网格列的百分比尺寸的基数,就是网格容器的content-box的width

617c3dabeff34106b79469021a4423e9.png

fr是一种单位,每个定义了fr尺寸的网格轨道会按fr比例分配网格容器的剩余可用空间。

比如上例中,将三个网格列尺寸分别定义为:1fr 2fr 1fr

即表示,将网格容器的剩余可用空间分为四等分,第一列、第三列占1分,第二列占2分。

咋一看,似乎fr尺寸和百分比尺寸没有区别,其实二者还是存在区别的。

百分比的基数是网格容器尺寸,fr是网格容器剩余空间的占比,所以二者基数不同。

我们通过下面例子来理解百分比尺寸和fr尺寸的区别:

d057252ab79b48b5bf8bccf3a700adac.png

上例中,为网格行之间、网格列之间添加都添加了10px的网格间距,通过结果可以发现:

  • 使用百分比尺寸的网格行的尺寸并没有因为网格间距的加入而改变,所以,网格行尺寸 + 网格间距,超出了网格容器范围;
  • 而网格列使用的是fr尺寸,它的基数是网格容器剩余空间,这里的剩余空间 = 网格容器尺寸 - 网格间距,这保证了网格列不会超出网格容器范围。

b8ae0f625fed4b21b8c5faf969694ccd.png

网格轨道尺寸设为auto,意思是占据网格容器剩余全部空间。

  • 百分比尺寸结合auto尺寸可以保证网格轨道不会超出网格容器范围,如上例中网格行尺寸设置。
  • 而fr尺寸也是基于网格容器剩余空间计算的,所以必然和auto尺寸存在冲突,而通过上例网格列可以发现,fr尺寸直接分配完了剩余空间,导致auto分配时,就没有剩余空间了。

需要注意的是虽然auto分得尺寸为0,但是也会生成网格轨道。如上例中,第二列左侧也有gap,而gap只存在于网格轨道之间。

min-content、max-content

Intrinsic Sizing In CSS - Ahmad Shadeed (ishadeed.com)

一个元素的width、height设置可以分为两种情况:

  • 外部尺寸(extrinsic)
  • 内部尺寸(intrinsic)

我们设置元素的width、height为固定尺寸,如100px,或者设置为百分比尺寸,如50%,都算是外部尺寸。

元素的外部尺寸的不会受自身内容的影响。(行内元素由于不可以设置width,所以不在此列。)

3de87944f57b4f45a43d2648743664a1.png

而有时候,我们期望元素的width、height可以适应其内容,CSS为此设计了三种特殊内部尺寸值:

  • min-content
  • max-content
  • fit-contnt

e144086e09b74bdf83629e7a8e52e7d3.gif

当一个元素的width为min-content时:

  • 如果内容为全英文,则width:min-content表示将元素宽度设为最长英文单词的长度
  • 如果内容为全中文,则width:min-content表示将元素宽度设为单个中文字符的长度
  • 如果内容为标签元素,则width:min-content表示将元素宽度设为最大宽度子元素的宽度

fb1cb7a97d274e5f937ba8f5078bac42.gif

当一个元素的width:max-content时,则表示将该元素的宽度设置为内容的宽度

dfd5e30257014194b838d5589514dc29.gif

当我们设置元素width:fit-content时,注意观察上例中盒子模型width的变化,发现:

元素可用空间,一般指的是元素所在父盒子内容区的宽高,例子中,元素可用空间即container容器的内容区宽度。

  • 当元素可用空间 availble >= 元素 max-content 时,则元素宽度为其max-content
  • 当元素可用空间 availble在 元素(mincontent,max-content) 之间 时,则元素宽度为avaible
  • 当元素可用空间 availble < 元素 min-content时,则元素宽度为其min-content

所以网格布局中,grid-template-rows或grid-template-columns轨道尺寸设置为min-content或max-content时,会根据轨道中网格项目内容来决定轨道实际尺寸。

769f62c0be6a4e4890a66e5e5839f565.png

上图网格容器中创建了一行一列,且网格行尺寸为max-content,即为网格项目内容实际高度,网格列尺寸为min-content,即为网格项目内容中最长的英文单词的长度。

minmax

minmax是css的函数,用于定义网格轨道的尺寸范围(闭区间);

minmax函数包含两个参数,依次是网格轨道的最小尺寸min、最大尺寸max,表示:

  • 当轨道可用空间 availble >= max尺寸时,轨道尺寸为max尺寸
  • 当轨道可用空间 availble in (min, max) 尺寸时,轨道尺寸为avaible尺寸
  • 当轨道可用空间 avaible <= min尺寸时,轨道尺寸为min尺寸

9e7d00b4bba74fb98d3bb28f06942bf8.png

326f60381a4b4d9690e217c1f6440eed.png

minmax的作用其实非常类似于元素内部尺寸fit-content,只是minmax的可以自定义最小和最大尺寸。

如果我们设置轨道尺寸为 minmax(min-content, max-content),其实就是设置轨道尺寸为fit-content,但是轨道尺寸却不支持fit-content。

872dc9a9e87345c7bd099fa6011fe987.png

db452a68fda7471795ad4f5ca5b12a89.png

minmax函数参数可以是固定尺寸,也可以是百分比尺寸,fr尺寸,auto尺寸,需要注意的是:

  • 如果 max尺寸 < min尺寸,则max尺寸被忽略,并且minmax函数结果为min尺寸。
  • fr尺寸不能作为 minmax的最小尺寸参数值,只能作为最大尺寸参数值
  • auto尺寸作为最大尺寸参数值时,等于max-content(存疑);作为最小尺寸时,等于轨道中单元格最小长宽 (由min-width/min-height) 的最大值(注意不等价于min-content)。

7c27ee0ca45d41698918c73d8104995d.png

如果minmax函数的最大尺寸参数值(如50px)小于最小尺寸参数值(如100px),则最大尺寸参数(如50px)作废,minmax函数结果固定是最小尺寸参数值(如100px)。

95b3c5686c094cd08587a844a3d46be3.png

可以发现,当将fr尺寸作为minmax函数最小尺寸参数时,则minmax无效。

当将fr尺寸作为minmax函数最大尺寸参数时,则等价于布局容器剩余空间。

bbcbc36abd564e69b9e14acdfc377ad3.png

5a3017d83b1f417ea30e4236a7eab37c.png

可以发现,minmax的最大尺寸参数设为auto时,并不等价于max-content,而是占满容器剩余空间。

0177ee364db248638d88441e1dbeb312.png

可以发现,minmax的最小尺寸参数设为auto时,等价于轨道中项目元素的min-width/min-height。

css - What happens when minmax is: minmax(auto, auto) - Stack Overflow

28407d2bec56403383e1a3b858e33a12.png

如果轨道中项目元素没有in-width/min-height,则auto等价于轨道的min-content。

repeat函数

如果我们的网格行都是等高的、网格列都是等宽,假设需要五行五列,则写法如下

grid-template-rows:1fr 1fr 1fr 1fr 1fr;

grid-template-columns:1fr 1fr 1fr 1fr 1fr;

如果需要十行十列,则

grid-template-rows:1fr 1fr 1fr 1fr 1fr 1fr 1fr 1fr 1fr 1fr;

grid-template-columns:1fr 1fr 1fr 1fr 1fr 1fr 1fr 1fr 1fr 1fr;

显然这种写法非常不友好,所以我们需要使用css的repeat函数。

css的repeat函数专门用于帮助grid-template-rows、grid-template-columns来生成重复尺寸。

repeat函数接收两个参数,第二个参数是要被重复的网格轨道尺寸,第一个参数是重复次数

75fe5521a1be444b958f7c1a669f17cc.png

需要注意的是,repeat函数的第二个参数既可以是一个尺寸,也可以多个尺寸

b308d35663b2469ca59cb6dc98db0c3c.png

如上例中,grid-template-columns:repeat(2, 2fr 3fr) 相当于生成 grid-template-columns: 2fr 3fr 2fr 3fr

还有需要注意的是,repeat函数的结果就是一串尺寸片段,我们可以继续在后面拼接新的尺寸

e8947d0fb9354abaa047b7d17f3b5f41.png

auto-fill、auto-fit

repeat函数的第一个参数还支持如下特殊值:

  • auto-fill
  • auto-fit

mdn上关于auto-fill、auto-fit的介绍非常晦涩,我们可以参考

[译] CSS Grid 之列宽自适应:`auto-fill` vs `auto-fit` - 掘金 (juejin.cn)

auto-fill、auto-fit的作用是啥?

  1. <!DOCTYPE html>
  2. <html lang="en">
  3. <head>
  4. <meta charset="UTF-8" />
  5. <meta http-equiv="X-UA-Compatible" content="IE=edge" />
  6. <meta name="viewport" content="width=device-width, initial-scale=1.0" />
  7. <title>Document</title>
  8. <style>
  9. .grid {
  10. display: grid;
  11. grid-template-columns: repeat(12, 1fr);
  12. }
  13. .grid > div {
  14. height: 100px;
  15. }
  16. </style>
  17. </head>
  18. <body>
  19. <div class="grid">
  20. <div>1</div>
  21. <div>2</div>
  22. <div>3</div>
  23. <div>4</div>
  24. <div>5</div>
  25. <div>6</div>
  26. <div>7</div>
  27. <div>8</div>
  28. <div>9</div>
  29. <div>10</div>
  30. <div>11</div>
  31. <div>12</div>
  32. </div>
  33. </body>
  34. </html>

上述代码,我们可以得到一个被分为12个等宽列轨道的网格容器。

并且网格容器的宽度是动态的,这意味着分给12个等宽列轨道的空间也是变化的。

此时为了避免列轨道的宽度太小,导致轨道项目元素的内容溢出,我们需要为列轨道设定一个最小宽度。

5b6f5b52641b44a585e37e7cd99b1362.png

grid-template-columns: repeat(12, minmax(100px, 1fr));

上面代码的意思是,将网格容器分为等宽的12列:

  • 如果(网格容器宽度 / 12) > 100px,则每列列宽为(网格容器宽度 / 12)
  • 如果 (网格容器宽度 / 12)<= 100px,则每列列宽为100px

这虽然可以保轨道列中项目元素不会被挤压变形,但是可能会导致部分网格轨道超出网格容器范围。

那么有没有一种办法,既能保证网格轨道不会超出网格容器范围,也可以保证网格列的最小宽度呢?

为了保证所有网格轨道的完整显示,只能将超出网格容器的网格单元换行显示。

即,此时网格容器上的轨道列数目应该是动态的,而不是固定的。

此时,我们可以使用repeat函数的auto-fill和auto-fit来实现动态轨道列数目。

96b012e46a604ed3a1252725657b0b4b.png

grid-template-columns: repeat(auto-fill, minmax(100px, 1fr));

上面代码的意思是,在网格容器的一行上尽可能多(即采用最小尺寸,如100px)地放入(不定数目auto-fill个)网格轨道,对于一行放不下的网格单元采取换行显示,保证其不会超出网格容器范围。

d2c0f9e58dbb4e178da37bfaea554fce.png

grid-template-columns: repeat(auto-fit, minmax(100px, 1fr));

auto-fit同理。

auto-fill与auto-fit的区别在于:

当网格容器的尺寸大于全部最小尺寸模式下的网格单元尺寸之和,比如网格容器1904px宽度,而12列等宽轨道最小尺寸之和为1200px,此时网格容器还剩余704px宽度空间:

  • 对于auto-fill来说,会再生成七列最小宽度(100px)轨道,然后将4px平分到19个轨道列宽度
  • 对于auto-fit来说,会直接将704px剩余空间平分给已有的12个轨道列

6786237f41b846baae40dab704a667f0.png

也就是说,auto-fill和auto-fit在:

  • 网格容器尺寸(如行宽)不足(放不下所有网格单元)时,表现相同,都会将超出网格容器的网格单元换行显示,

6016d578591944e2ba69f923082fff8b.png

  • 网格容器尺寸(如行宽)充足(可以放下所有网格单元)时,对于网格容器剩余空间的处理不同,auto-fill会继续创建轨道,直到剩余空间不足轨道尺寸,然后再将剩余空间按比例分配给各轨道;而auto-fit会直接将剩余空间按比分配给已有轨道。

网格单元

网格行与网格列相交便形成了网格单元,网格单元是grid布局的最小单元。网格单元和网格轨道一样也并非真实DOM元素,只是一种逻辑概念,所以它不会被浏览器渲染出来。

如上小节例子中,网格容器被分为3行3列,9个网格单元。

网格项目

网格容器元素的直接子元素就是网格容器的网格项目,类似于弹性容器与弹性项目的关系。

默认情况下,网格项目会按代码顺序依次放入网格容器的网格单元中。

90a1bad0d9cc4e1aad06103f5dfdf64e.png

网格线

网格轨道是由网格线组成的,每个网格轨道由两条网格线组成。网格线不是真实DOM,所以不会被浏览器渲染。

网格线默认通过数字索引(从左往右,从上往下,起始索引1)引用,也可以通过自定义名字来引用。

903eb895ceca4c3e9e53014d93942b39.png

9979e0679129407390a7be37f1fff428.png

关于为网格线自定义名字:

grid-template-rows、grid-template-colums用于定义网格轨道,而每个网格轨道由两条网格线构成;

grid-template-rows、grid-template-colums 不仅支持网格轨道数目、尺寸,还支持定义组成网格轨道的网格线的名字,语法如下:

grid-template-rows:[lineName1] size1 [lineName2] size2 [lineName3] …

特点就是一根网格轨道夹在两根网格线之间。

对于使用repeat函数生成的网格轨道,我们可以

grid-template-rows:repeat(3, [r-start] size [r-end])

此时实际生成结果为

grid-template-rows:[r-start 1] size [r-end 1] [r-start 2] size [r-end 2] [r-start 3] size [r-end 3]

此时网格线 r-end 1和r-start 2其实同一条网格线。

网格线占位

之前我们说过网格单元是被网格容器中的网格轨道分隔出来的,而本质上来说,网格单元其实是被网格容器中的网格线分割出来的。

我们可以用四条网格线来描述网格容器中的任意一个网格单元或多个网格单元。网格线引用包括:

  • grid-row-start
  • grid-row-end
  • grid-column-start
  • grid-column-end

这四个样式都属于网格项目元素,而不是网格容器。

网格项目元素通过四条网格线,来设定自身占据那个网格单元。

如下图,通过默认的数字索引网格线,来定义网格项目位置。

0c6590a4e22049d9aebc15e964e54146.png

如下图通过自定义网格线名字来定位网格项目位置1fbc725ea9284065a1016e7ce3e5f526.png

如下通过repeat下的网格线自定义名字来定位网格项目位置。

df6296cebdb14fe29df81b230734f5d2.png

上面设定网格线的方式比较繁琐,我们可以进行简化,即使用复合语法:

  • grid-row:grid-row-start / grid-row-end
  • grid-column: grid-column-satrt / grid-column-end

22ffe1701e1f445b95b0063e696a0981.png

7f34e6fcd2aa4c2ab7c9941d108ae83f.png

其实,我们还可以继续简化网格线引用,即使用进一步的复合语法:

grid-area:grid-row-start / grid-column-start / grid-row-end / grid-column-end

c349b15f5de845f19203df56f0c733dd.png

我们需要注意的是,网格线不仅可以描述一个网格单元,还可以描述多个网格单元,即让一个网格项目占据多个网格单元

8da3ad2ca1034f2ebe4a8de0316ba1be.png

另外,我们目前都是基于四条网格线来完成的网格单元描述,这种描述方式其实不太好在脑海中想象,我们可以只定义两条起始网格线,另外两条结束网格线通过偏移来描述。

064c90bf501d4c7c85bc3d2c0500cfa3.png

grid-row-end:span 2; // 含义是相对于grid-row-start网格线向下偏移两个网格单元

grid-column-end:span 2; // 含义是相对于grid-column-start网格线向右偏移两个网格单元

支持简写形式

ba6ad5f6cb28480c8a7fbd0f3987af1c.png

网格区域占位

通过网格线描述网格单元其实使用起来语义化非常差

grid-area: 1 / 2 / span 2 / span 2

比如第一眼看到上面样式,我们很难直观感受到该项目所处网格单元的位置。

所以,网格容器提供了 grid-template-areas 样式直接描述网格单元,网格项目通过grid-area直接引用网格区域即可。

c5d1736502df4a48bc77904ab972ff5b.png

如果网格区域对应多个网格单元,则直接将处于同一个网格区域内的网格单元区域名称改为一致即可。

26b2a700d8ab454ab775863a0541a646.png

对于用不到区域命名的网格单元,我们可以用.来作为其区域名

5d9c714c2a3d426785cf168809a55d32.png

grid-template

grid-template是grid-template-rows、grid-template-columns、grid-template-areas的复合写法。

下面以三行三列的网格容器定义为例:

grid-template复合写法一:网格区域 + 网格轨道

grid-template:”grid-template-areas#1” grid-template-rows#1

“grid-template-areas#2” grid-template-rows#2

“grid-template-areas#3” grid-template-rows#3 / grid-template-columns;

90a4dfbe47b949f0a1e8c16457e8ae5a.png

grid-template复合写法二:网格线 + 网格轨道

grid-template: grid-template-rows / grid-template-columns

79284297cf06458cbdb89678952e284b.png

grid-template复合写法三:网格线 + 网格区域 + 网格轨道

grid-template:[line1] “grid-template-areas#1” grid-template-rows#1 [line2]

[line3] “grid-template-areas#2” grid-template-rows#2 [line4]

[line5] “grid-template-areas#3” grid-template-rows#3 [line6] / grid-template-columns;

e580f5eb77eb4b6783fabb0ec2943f68.png

grid-template:none

表示网格行列隐式生成,使用grid-auto-rows、grid-auto-columns来定义网格行列尺寸。

显示网格、隐式网格

我们通过 grid-template-columnsgrid-template-rows 创建出来的轨道,称为显示网格(轨道);但是这些轨道不一定能容纳所有的网格项目, 浏览器根据网格项目的数量计算出来需要更多的轨道, 就会自动生成新的轨道来容纳多出来的网格项目, 而这些自动生成的轨道被称为隐式网格(轨道)。

另外,如果我们未给网格容器定义显示网格轨道,则浏览器会根据网格项目自动创建隐式网格轨道。

隐式网格轨道的尺寸通过如下属性控制:

  • grid-auto-rows:用于指定隐式创建的行轨道尺寸,默认值auto
  • grid-auto-columns:用于指定隐式创建的列轨道尺寸,默认值auto

我们可以将这两个属性理解为网格轨道的默认尺寸设置,即:

  • 如果未定义网格容器的grid-template-rows和grid-template-columns,则网格轨道尺寸采用grid-auto-rows和grid-auto-columns
  • 如果已定义网格容器的grid-template-rows和grid-template-columns,但是显示网格轨道不足以放下所有网格项目元素,则自动创建的隐式轨道的尺寸会采用grid-auto-rows或grid-auto-columns

需要注意的是,隐式创建的轨道只能是行轨道或列轨道之一,不可能同时创建两个方向的隐式轨道,而具体创建哪个方向的隐式轨道,由属性 grid-auto-flow 决定,该属性有如下值:

  • row(默认值)
  • column

当grid-auto-flow:row时,只能创建隐式行轨道。

当grid-auto-flow:column时,只能创建隐式列轨道。

grid-auto-flow 还决定了 网格容器中 网格项目元素的排列方向

  • row:该关键字指定自动布局算法按照通过逐行(从左到右)来排列元素,在必要时增加新行(隐式网格)
  • column:该关键字指定自动布局算法通过逐列(从上到下)来排列元素,在必要时增加新列(隐式网格)

d11966698f0340e8844a50eb44365758.png

上例中,由于网格容器没有定义显示网格轨道,所以默认采用隐式网格轨道尺寸,然而这里也没有自定义隐式网格轨道,因此采用默认的隐式网格轨道尺寸auto。auto的含义是占据剩余空间。

这里因为grid-auto-flow是row方向,所以网格项目逐行填充,必要时新增行,列数目不会变化,因此grid-auto-columns:auto会产生占据网格容器全部宽度的唯一列,而grid-auto-rows:auto会产生平分网格容器高度的多个行。

e169f4bf5a18493088428b5ec95e46e2.png

grid-auto-flow:column同理

a3426826e7f242209abf220f074c98fa.png

上例中,网格容器只有2行2列,理论只能放4个项目元素,但是实际上有9个项目元素,所以必然有5个项目元素放不下,此时只能生成隐式网格轨道来放。

需要注意的是,此时grid-auto-flow:row决定了只会创建隐式网格行轨道,不会创建隐式网格列轨道。

我们可以这样理解:

grid-auto-flow: row; // 只会新增行,不会新增列

grid-template-rows: repeat(2, 100px); // 显示行使用显示网格尺寸

grid-auto-row: auto; // 隐式行使用隐式网格轨道尺寸

grid-template-columns: repeat(2, 100px);

grid-auto-columns: auto; // 没有新增列,所以不需要使用隐式网格轨道尺寸

8cf149a66e3e4deea0f203af7b855b79.png

密集模式

grid-auto-flow不仅可以控制网格项目的排列方向及隐式网格创建,还可以控制网格项目的包装模式。

grid-auto-flow实际上有四种值:

  • row
  • row dense
  • column
  • column dense

其中row、column是稀疏包装模式,row dense、column dense是密集包装模式。

我们通过几个例子来理解稀疏包装模式和密集包装模式:

  1. <!DOCTYPE html>
  2. <html lang="en">
  3. <head>
  4. <meta charset="UTF-8" />
  5. <meta http-equiv="X-UA-Compatible" content="IE=edge" />
  6. <meta name="viewport" content="width=device-width, initial-scale=1.0" />
  7. <title>Document</title>
  8. <style>
  9. .grid {
  10. display: grid;
  11. grid-template-rows: repeat(4, 100px);
  12. grid-template-columns: repeat(4, 100px);
  13. }
  14. .red {
  15. grid-area: 1 / 2 / 2 / 3;
  16. background-color: red;
  17. }
  18. .blue {
  19. grid-area: 2 / 1 / 3 / 3;
  20. background-color: blue;
  21. }
  22. .green {
  23. grid-row: 1 / 3;
  24. background-color: green;
  25. }
  26. .yellow {
  27. grid-row: 1 / 2;
  28. background-color: yellow;
  29. }
  30. </style>
  31. </head>
  32. <body>
  33. <div class="grid">
  34. <div class="red">1</div>
  35. <div class="blue">2</div>
  36. <div class="green">3</div>
  37. <div class="yellow">4</div>
  38. </div>
  39. </body>
  40. </html>

b478139fe03647bba099159e84718b4a.png

上面例子中,网格容器就是稀疏包装模式,因为默认的grid-auto-flow是row,此时我们发现yellow项目只需要grid-row: 1 / 2,即在第一行就行,而第一行刚好还剩两个空白网格单元,但是yellow项目却没有被放入第一个空白网格单元中。

这种情况就是稀疏包装模式导致的。

如果我们将grid-auto-flow改为row dense,则可以让yellow项目放入第一个空白网格单元中。

65b269faea3747c4a5a56b07f44da846.png

还有一个例子

  1. <!DOCTYPE html>
  2. <html lang="en">
  3. <head>
  4. <meta charset="UTF-8" />
  5. <meta http-equiv="X-UA-Compatible" content="IE=edge" />
  6. <meta name="viewport" content="width=device-width, initial-scale=1.0" />
  7. <title>Document</title>
  8. <style>
  9. .grid {
  10. display: grid;
  11. grid-template-rows: repeat(4, 100px);
  12. grid-template-columns: repeat(4, 100px);
  13. }
  14. .red {
  15. grid-area: 1 / 2 / 2 / 3;
  16. background-color: red;
  17. }
  18. .blue {
  19. grid-column: 2 / 3;
  20. background-color: blue;
  21. }
  22. .green {
  23. grid-column: 1 / 2;
  24. background-color: green;
  25. }
  26. </style>
  27. </head>
  28. <body>
  29. <div class="grid">
  30. <div class="red">1</div>
  31. <div class="blue">2</div>
  32. <div class="green">3</div>
  33. </div>
  34. </body>
  35. </html>

bae239a4564544b789a5c3ef58538566.png

green项目只需要占据grid-column: 1 / 2,即第一列中某个网格单元,但是green却没有占据第一行第一列网格单元,这也是稀疏包装模式导致的。

我们只需要让grid-auto-flow变为row dense即可

838558e65d734e0cb041f265e1d50a45.png

至于稀疏包装模式为什么会导致上述现象,这涉及到网格项目自动放置算法,可以借鉴如下资料

CSS 网格布局中的自动定位 - CSS(层叠样式表) | MDN (mozilla.org)

css网格_CSS网格中自动放置算法的循序渐进指南 - 灰信网(软件开发博客聚合) (freesion.com)

我这边做了一个半成品总结:

通过前面两个例子,大家可以发现,空白网格单元的出现,必然伴随着 定行不定列 或者 定列不定行 的网格项目,所谓

定行不定列:即只定义了grid-row,未定义grid-column

定列不定行:即只定义了grid-column,未定义grid-row

无论是稀疏模式,还是密集模式,定行不定列项目、定列不定行项目都不会占用已被占用的网格单元,即它们都不会和其他项目共享网格单元,如果它们首选的网格单元已被占用,则会换行或换列找下一个符合要求的网格单元。

最关键的是,稀疏模式下定行不定列项目之间、定列不定行项目之间会产生约束,在DOM中,

  • 后渲染的定行不定列项目 一定排在 先渲染的定行不定列项目 之后
  • 后渲染的定列不定行项目 一定排在 先渲染的定列不定行项目 之后

这是产生空白网格的关键原因。

而密集模式下,上面两条约束就失效了。

网格间距

网格间距指的是网格轨道之间的间距,而不是网格项目之间的间距。

网格间距可以通过网格容器上的样式属性定义:

  • row-gap:行轨道之间的间距
  • column-gap:列轨道之间的间距

afc97c95069f47be8501c180d074b3fc.png

我们需要注意的是,如果网格容器具有固定width、height的话,则网格间距的加入可能会导致网格轨道溢出

aa5bb462210947878f5cf92731017c3e.png

如上例中,网格容器内容区是300*300尺寸,而网格容器的行轨道尺寸之和是300px,列轨道尺寸之和也是300px,刚好填满网格容器内容区。但是此时,我们又给行轨道之间加入10px间距,列轨道之间加入10px间距,这就导致网格系统多出了水平、垂直方向各多出了20px,导致了网格轨道溢出网格容器。

所以我们在使用网格间距时,一定要注意设置网格轨道的尺寸要兼容网格间距,避免溢出。

我们可以在计算网格轨道尺寸时,手动去除掉网格间距尺寸,但是这种计算太麻烦

4ef02225f4b24b2783fa8ff21d48a13b.png

因此,我们可以借助fr尺寸,auto尺寸来实现网格轨道尺寸自动减去网格间距

ac99d7a8f1af40a4bcf88fe97b82a8dd.png

最后,我们的row-gap、column-gap还可以复合写为gap:row-gap column-gap

05129d2f68394a17bee570290bb1ece7.png

网格单元的对齐

f7699f23c9b8442eb81b053d24c7e909.gif

如果网格容器划分完轨道后,依旧还能剩余空闲空间,如上例中,网格容器300px*300px,列轨道尺寸总计200px,行轨道尺寸总计100px,则在垂直方向上还剩200px空闲空间,在水平空间上还剩100px空闲空间。

此时我们就可以基于网格容器,对网格单元进行水平方向、垂直方向的对齐操作。

  • justify-content:start | center | end | space-around | space-between | space-evenly
  • align-content:center | space-around | space-between | space-evenly | stretch

具体效果看上面动图。

另外,justify-content、align-content可以复合写为

place-content:align-content justify-content

网格项目的对齐

网格项目的对齐,即网格项目在网格单元中的对齐。它分为两类:

  • 所有网格项目设置统一对齐方式
  • 某个网格项目设置单独对齐方式

统一对齐,需要在网格容器上设置如下属性:

  • justify-items:stretch | start | center | end
  • align-items:stretch | start | center | end | baseline

justify-items、align-items默认值为stretch,即当网格项目未定义width、height时,默认被拉伸填充网格单元。

统一对齐对网格容器中所有网格项目有效。

0c92d77687cd41d387a35fe4e8146914.gif

另外,justify-items、align-items可以复合写为

place-items:align-items justify-items

单独对齐,需要在网格项目上设置如下属性:

  • justify-self:stretch | start | center | end
  • align-self:stretch | start | center | end | baseline

justify-self、align-self的功能和justify-items、align-items基本一致,只是justify-self、align-self只能作用于所在的网格项目。

b1e38600ca8a401bb0aba010153aad98.gif

另外, justify-self、align-self可以复合写为

place-self:align-self justify-self

发表评论

表情:
评论列表 (有 0 条评论,38人围观)

还没有评论,来说两句吧...

相关阅读