Grid 网格布局

什么是网格布局?

网格仅仅是由水平和垂直线集合创建的一个模式,我们可以根据这个模式排列我们的设计元素。它们帮助我们创建设计——在页面之间移动时元素不会跳动或更改宽度,从而在我们的网站上提供高一致性。

平常我们使用的 Flex Layout 弹性盒模型,是一次革命性的突破,解决传统布局方案上的三大痛点 排列方向、对齐方式、自适应尺寸,是目前最为成熟和强大的布局方案。

而 Grid Layout 是一种基于二维网格的布局系统,旨在完全改变我们设计基于网格的用户界面的方式,弥补网页开发在二维布局能力上的缺陷。

网格通常具有列(column),行(row),以及在每行和列之间的间隙——通常称为沟槽(gutter)。

网格容器 (Grid Containers)

通过 display 属性给一个元素显式的设置了 grid 或者 inline-grid 属性值,这个元素将自动变成网格容器。

一个网格容器将会创建一个新的 网格格式化上下文内容(grid formatting context)。除是网格布局代替了块布局之外,网格格式化上下文和块格式化上下文是相同的。

浮动对网格容器不会有影响,而且网格容器的 margin 不会和内容的 margin 相互层叠。

因为网格容器不是块容器,所以一些属性在网格布局中将会失效:

  • 多栏布局模块中的所有“column-*”属性运用在网格容器上将失效。
  • “float”和“clear”使用在网格项目上将失效
  • “vertical-align”使用在网格项目上将失效
  • “::first-line”和“::first-letter”这样的伪元素不能应用在网格容器上。

注意:如果一个元素指定了 display 值为 inline-grid,并且此元素具有 float 或绝对定位时,这个元素将的 display 值将会以 grid 显示。

display

通过display属性设置属性值为 grid 或 inline-grid 可以创建一个网格容器。网格容器中的所有子元素就会自动变成网格项目(grid item),网格项目默认放在行中,并且跨网格容器的全宽。

  • grid: 块级的网格容器
  • inline-grid: 内联的网格容器

测试: 请点击按钮改变布局

1
2
3
Other Text

行和列 (grid-template-columns/rows)

使用 grid-template-columnsgrid-template-rows 属性可以显式的设置一个网格的列和行。

可以设置任何非负值,可以是px、%、em等长度单位的值,也可以是fr、repeat()、auto等值。

测试: 请点击按钮改变布局

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32

grid-template-columns

grid-template-rows

网格间距 (grid-row/column-gap)

grid-gapgrid-row-gapgrid-column-gap 两个属性的缩写。

如果它指定了两个值,那么第一个值是设置 grid-row-gap 的值,第二个值设置 grid-column-gap 的值。

如果只设置了一个值,表示行和列的间距相等,也就是说 grid-row-gapgrid-column-gap 的值相同。

间距(Gap)可以设置任何非负值,长度值可以是px、%、em等单位值。

注意: grid-gap 只能创建列与列或行与行之间的间距,但不能创建列和行与网格容器边缘的间距。

测试: 请点击按钮改变布局

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16

grid-gap (grid-row-gap grid-column-gap)

隐式网格 (grid-auto-rows/columns)

当网格项目确认在显式网格之外时就会创建隐性网格,当没有足够的空间或者显式的网格轨道来设置网格项目,此时网格项目就会自动创建隐式网格。

使用 grid-auto-rowsgrid-auto-columns 属性可以定义隐式的网格。

grid-template-rows: 70px;
grid-template-columns: repeat(2, 1fr);
grid-auto-rows: 140px;

在这个例子中我们只定义了一行(轨道),所以 item1 和 item2 的高都是 70px。

第二行(轨道)自动创建了 item3 和 item4 空间。grid-auto-rows 定义隐式网格中的行(轨道)的大小,因此 item3 和 item4 的高度是 140px。

1
2
3
4

网格流方向 (grid-auto-flow)

使用 grid-auto-flow 可以定义网格流的方向,可选 columnrow,默认为 row

比如将网格流设置为 column:

grid-auto-flow: column;
1
2
3
4

配合隐式网格使用:

grid-template-columns: 30px 60px;
grid-auto-flow: column;
grid-auto-columns: 1fr;

在这个例子中,我们只定义了前两列的轨道尺寸。item1的尺寸是30px,item2的尺寸是60px。

使用grid-auto-flow:column自动创建了隐式网格,用来放置item3、item4和item5,并且这三个列(轨道)的尺寸由grid-auto-columns来定义。

1
2
3
4

网格项目 (Grid Items)

在一个网格容器中包含了多个网格项目。网格容器的子元素称为网格项目以及运行在网格容器的文本将自动变成一个匿名的网格项目,然后如果只是一个空格,这个匿名项目就相当于 display:none 一相被隐藏在网格容器之中。

一个网格项目创建一个新的格式化上下文内容。这种格式化上下文内容类型取决于它的 display 值。

网格项目层级 (z-index)

如果有多个项目在同一个网格区域,可以通过 z-index 设置其层级,若不设置后面的网格项目会覆盖在前面的网格项目之上。

<div class='items'>
  <div class='item' v-for='item in 2'>{{item}}</div>
</div>
.items {
  display: grid;
  grid-template-columns: 1fr 1fr;
  grid-template-rows: 100px 100px;
  .item:nth-child(1) {
    grid-area: 1 e('/') 1 e('/') 2 e('/') 2;
    background: rgba(255, 0, 0, .4);
    z-index: 1;
  }
  .item:nth-child(2) {
    grid-area: 1 e('/') 1 e('/') 3 e('/') 3;
    background: rgba(0, 255, 0, .4);
  }
}
1
2

网格项目对齐方式 (Box Alignment)

网格轨道对齐可以相对于网格容器行和列轴。

  • justify-content 指定网格轨道沿着列轴对齐方式
  • align-content 指定网格轨道沿着行轴对齐方式

他们均包含以下几种属性值:

  • start 指定列沿着轴开始排列,justify-content 和 align-content 的默认值
  • end 列沿着轴末端开始排列
  • center 列沿着轴中间排列
  • space-around 网格容器剩余列空间分配给每列的两端
  • space-between 网格容器剩余列空间平均分配给相邻的两行/列
  • space-evenly网格容器剩余空间平均分配给行/列之间,相邻两行/列之间的间距与第一行/列和容器最左侧边级和最后一行/列与容器最右侧边缘间距相同
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16

justify-content

align-content

网格线 (Grid Lines)

网格线实际上是代表线的开始、结束,两者之间就是网格列或行。

每条线是从网格轨道开始,网格的网格线从1开始,每条网格线逐步增加1。

网格线包括:

  • grid-row-start
  • grid-row-end
  • grid-column-start
  • grid-column-end
.item1 {
  grid-row-start: 2;
  grid-row-end: 3;
  grid-column-start: 2;
  grid-column-end: 3;
}

如上,创建了个两列三行的网格,包括三条列网格线和四条行网格线。item1 就是由行和列的号码重新定位。

如果一个网格项目跨度只有一行或一列,那么 grid-row-endgrid-column-end 不是必需的。

grid-row 和 grid-column

grid-rowgrid-row-startgrid-row-end 的组合属性

如上面 item1 的属性可以设置为:

grid-row: 2 / 3;

grid-columngrid-column-startgrid-column-end 的组合属性

如上面 item1 的属性可以设置为:

grid-column: 2 / 3;

grid-rowgrid-column 的设置规则:

  • 如果只提供一个值,他指定了grid-row-start和grid-column-start的值。
  • 如果提供两个值,第一个值是grid-row-start、grid-column-start的值,第二个值是grid-row-end、grid-column-end的值,两者之间必须要用/隔开。

跨行跨列 (grid-area)

默认情况下网格项目跨度只有一个列和行,但可以跨越多个行和列。著作权归作者所有。

可以通过设置 grid-column-endgrid-column-start 距离多个网络线号实现多个列跨越,通过 grid-row-endgrid-row-start 距离多个网格号实现多个行跨越。

grid-areagrid-row-startgrid-column-startgrid-row-endgrid-column-end 的简写

比如

grid-area: 2 / 2 / 5 / 4;
// 同
grid-row: 2 / 5;
grid-column: 2 / 4;
// 同
grid-row-start: 2;
grid-column-start: 2;
grid-row-end: 5;
grid-column-end: 4;

设置了一个跨越三行两列的网格项目

1
2
3
4
5
6
7

使用关键词 span 后面紧随数字,表示合并多少个列或行:

grid-row: 2 / span 3;
grid-column: span 2;

上面的示例,表示从第二行开始,合并之后的3行,从第一列来时,合并之后的2列。

1
2
3
4
5
6
7

网格线命名

通过 grid-template-rows 和 grid-template-columns 定义网格时,网格线可以被命名。网格线名称也可以设置网格项目位置,并用来定位网格项目的位置。

分配网格线名称必须用方括号 [网格线名称],然后后面紧跟网格轨道的尺寸值。定义网格线名称时需要避免使用规范中出现的关键词,以免导致混乱。

grid-template-rows: [row-1-start] 1fr [row-2-start] 1fr [row-2-end];
grid-template-columns: [col-1-start] 1fr [col-2-start] 1fr [col-3-start] 1fr [col-3-end];

可以在方括号中添加多个名称来命名网格线名称,使用多外名称命名网格线名称时,名称间要用空格隔开。

grid-template-rows: [row-start row-1-start] 1fr [row-1-end row-2-start] 1fr [row-2-end row-end];
grid-template-columns: [col-start] 1fr [col-2-start] 1fr [col-3-start] 1fr [col-end];

通过网格线名称设置网格项目位置

使用网格线名称设置网格项目位置和使用网格线号码设置网格项目位置类似。

grid-row-start:    row-2-start;
grid-row-end:      row-end;
grid-column-start: col-2-start;
grid-column-end:   col-end;

grid-row: row-2-start / row-end;
grid-column: col-2-start / col-end;

引用网格线名称的时候不应该带方括号。

使用相同的名称命名网格线和设置网格项目位置

使用repeat()函数可以给网格线分配相同的名称。这可以节省一定的时间。

grid-template-rows: repeat(3, [row-start] 1fr [row-end]);
grid-template-columns: repeat(3, [col-start] 1fr [col-end]);

使用repeat()函数可以给网格线命名,这也导致多个网格线具有相同的网格线名称。

相同网格线名称指定网格线的位置和名称,也且会自动在网格线名称后面添加对应的数字,使其网格线名称也是唯一的标识符。

grid-row: row-start 2 / row-end 3;
grid-column: col-start / col-start 3;

使用相同的网格线名称可以设置网格项目的位置。网格线的名称和数字之间需要用空格分开。

在这个示例中,item1放置位置是row-start第2条开始,至row-end的第3条结束,这用来设置item1在行的起始和结束位置;col-start的第1条开始,至col-start的第3条结束(col-start的第3条也对应的是col-end的第2条),用来设置item1在列的起始位置和结束位置。

网格线负值

网格线可以使用负值,表示从右向左的顺序。

比如:

grid-row: 1 / -2;
grid-column: 1 / -2;
1
2
3
4
5
6
7

网格区域 (Grid Areas)

网格区域是一个逻辑空间,主要用来放置一个或多个网格项目。他有四条网格线,网格区域每边一条,四边相交组织的网格轨道可以调整网格区域大小。

grid-areagrid-rowgrid-column 的组合属性

grid-area: 2 / 2 / 3 / 3;

grid-area 的设置规则:

指定四个值,第一个值对应 grid-row-start,第二个值对应 grid-column-start,第三个值对应 grid-row-end 和第四个值对应 grid-column-end

网格区域命名

像网格线名称一样,网格区域的名称也可以使用grid-template-areas属性来命名。引用网格区域名称也可以设置网格项目位置。

grid-template-areas:
  "header header"
  "content sidebar"
  "footer footer";
grid-template-rows: 150px 1fr 100px;
grid-template-columns: 1fr 200px;

设置网格区域的名称应该放置在单引号或双引号内,每个名称由一个空格符分开。

网格区域的名称,每组(单引号或双引号内的网格区域名称)定义了网格的一行,每个网格区域名称定义网格的一列。

通过网格区域命名来定位网格项目

也可直接通过命名的网格区域指定网格项目所在的位置。

使用 grid-row-startgrid-row-endgrid-column-startgrid-column-end 可以引用网格区域名称,用来设置网格项目位置。

grid-row-start: header;
grid-row-end: header;
grid-column-start: header;
grid-column-end: header;

可以使用简写:

grid-row: footer;
grid-column: footer;

grid-area简写属性也可以引用网格区域的名称来设置网格项目的位置。

grid-area: header;

如果使用 grid-template 属性为网格容器设置的网格区域,此时设置 grid-template-rowsgrid-template-columns 将无效。

<div class='demo1'>
  <div class='container'>
    <div class='items'>
      <div class='item'>header</div>
      <div class='item'>content</div>
      <div class='item'>sidebar</div>
      <div class='item'>footer</div>
    </div>
  </div>
</div>
.demo1
  .container
    .items
      grid-template "header header" "content sidebar" "footer footer"
      .item
        &:nth-child(1)
          grid-area header
        &:nth-child(2)
          grid-area content
        &:nth-child(3)
          grid-area sidebar
        &:nth-child(4)
          grid-area footer
header
content
sidebar
footer

隐式地命名网格区域名称

通常可以将网格线命名成任何你想命名的名称,如果网格线名称添加-start和-end的后缀,其实也隐式的创建一个网格区域,可以用来设置网格项目的位置。

grid-template-rows: [outer-start] 1fr [inner-start] 1fr [inner-end] 1fr [outer-end];
grid-template-columns: [outer-start] 1fr [inner-start] 1fr [inner-end] 1fr [inner-end];

在这个示例中,行和列都具有 inner-startinner-end 网格线名称,同时也对应的创建一个隐式网格区域名称 inner

grid-area: inner;

网格项目定位可以通过网格区域名称来设置,而不需要使用网格线名称。

隐式命名网格线名称

隐式的指定网格线反向指定了隐式的网格区域名称,命名的网格区域隐式的命名了网格线名称。

grid-template-areas:
  "header header"
  "content sidebar"
  "footer footer";
grid-template-rows: 80px 1fr 40px;
grid-template-columns: 1fr 200px;

指定网格区域会给网格区域边线添加隐式的网格线名称。这些网格线的命名是基于网格区域来命名,只是在网格区域名称的后面添加后缀 -start-end

grid-row-start: header-start;
grid-row-end: content-start;
grid-column-start: footer-start;
grid-column-end: sidebar-end;

在这个示例中,header通过隐式的网格线名称设置其位置。

新增属性值

弹性长度 fr

可以使用单位 fr 自动按照比例计算宽高。

比如设置

grid-template-columns: 1fr 1fr 2fr

在这个示例中,将网格容器分成了4等份(1 + 1 + 2 = 4),每一份(1fr)是网格容器宽度的四分之一。所以 item1 和 item2 的宽度是网格容器的四分之一宽,item3 是网格容器宽度的四分之二(2fr)

当fr和其它长度单位的值结合在一起的时候,fr 是基于网格容器可用空间来计算。

grid-template-columns: 3rem 25% 1fr 2fr

在这个示例中,将网格容器可用空间是网格宽度减去 3rem 和 25% 剩下的宽度,而 fr 就是基于这个尺寸计算:

1fr = (网格宽度 - 3rem - 网格宽度 * 25%) / 3

minmax

minmax() 函数能够让我们用最简单的 CSS 控制网格轨道的大小。这个函数包括一个最小值和最大值。

如果定义的最大值小于最小值,它将会被忽略,函数会被视为只设置了一个最小值。

minmax()函数接受六种类型的值:

- 长度值 `<length>`
- 百分比值
- 弹性值
- max-content
- min-content
- auto

示例代码

.grid {
  display: grid;
  grid-template-columns: minmax(100px, 200px) 1fr 1fr;
}

repeat

使用repeat()可以创建重复的网格轨道。这个适用于创建相等尺寸的网格项目和多个网格项目。repeat()接受两个参数:第一个参数定义网格轨道应该重复的次数,第二个参数定义每个轨道的尺寸。

比如:

grid-template-columns: 30px repeat(3, 1fr) 30px;

在这个示例中,第一列和最后一列的宽度都是 30px,并且它们之间有另列三列,这三列是通过 repeat() 来创建的,而且每列的列宽是 1fr(1fr = (网格宽度 - 30px - 30px) / 3)

甚至,在 repeat() 中,也可嵌套 minmax(),控制每一个网格项目的最大最小值。

.grid {
  display: grid;
  grid-template-columns: repeat(3, minmax(200px, 1fr));
}

这个例子中,每一行分为三列,每一列最小宽度为 200px,最大时平分网格宽度。

auto-fit

在 repeat() 函数中使用 auto-fit 这个关键词,来替代重复次数。这可以根据每列的宽度灵活的改变网格的列数,灵活的控制网格的列数。

.grid {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(200px, 1fr));
}

这个例子中,网格中的每个列的最小宽度为200px。根据浏览器视窗大小,网格数量会根据共最合理的宽度进行变化。做到了不使用媒体查询即可实现响应式布局。

max-content 和 min-content

max-content关键词是一个特殊的值,它代表了单元格“最理想的大小”。网格单元格最小的宽度围绕它的内容。例如,如果单元格的的内容是一个句子,理想的单元格的宽度将是整个句子的长度,无论是什么长度,单元格内容的句子都不会换行。

.grid {
  display: grid;
  grid-template-columns: minmax(max-content, max-content) 1fr 1fr;
}

min-content 代表单元格最小宽度,可以不让内容溢出单元格,除非是不可避免的。

.grid {
  display: grid;
  grid-template-columns: minmax(min-content, min-content) 1fr 1fr;
}

auto

auto,minmax() 函数的最小值和最大值使用 auto,将会有不同的含义,其含义也取决于 minmax() 函数的最小值和最大值。

如果用于最大值,那么 auto 值相当于 max-content 值;如果用于最小值,那么 auto 值相当于 min-content。最大的最小尺寸不同于 min-content,它通过 min-width 或 min-height 来指定。

.grid {
  display: grid;
  grid-template-columns: minmax(auto, auto) 1fr 1fr;
}

参考资料

MIT Licensed | Copyright © 2018-present 滇ICP备16006294号

Design by Quanzaiyu | Power by VuePress