Grid 布局
概念
Grid 布局即为二维布局,这个应该是目前 Web 布局方式的”顶配“了。它的描述方式是:
- 创建一个二维网格。用一些水平、垂直的轨道(Tracks)画出一个二维网格。
- 画好网格之后,内部的子元素就可以进行定义自己要处于的什么位置。这就好比在一个直角坐标系上,要画一个矩形一样。描述这个矩形的位置有很多种方式。比如,我们只需要知道这个矩形两个对角点的坐标即可;或者我们也可以通过指明这个矩形四条边的位置来描述这个矩形。
例子:圣杯布局
我们运行一个典型的圣杯布局的例子,先有些感想认知。
<!-- pg-title: Grid example -->
<!-- pg-auto-run: true -->
<header>Nav Bar</header>
<aside id='toc'>Table Of Content </aside>
<article>Content</article>
<aside id='ads'>Ads</aside>
<footer>Footer</footer>
* {
margin: 0;
padding: 0;
box-sizing: border-box;
}
body {
display: grid;
height: calc(100vh - 80px);
grid-template-rows: 50px 1fr 50px;
grid-template-columns: 1fr 960px 1fr;
grid-gap: 10px;
margin: 40px; /* 为了用 Chrome Inspect 的时候看清轨道编号 */
background: #EEEEEE;
}
header, aside, article, footer {
background: white;
color: black;
display: flex;
justify-content: center;
align-items: center;
font-size: 2rem;
}
header {
grid-column: 1 / 4;
grid-row: 1 / 2;
}
aside#toc {
grid-area: 2 / 1 ;
}
article {
grid-column: 2 / 3;
grid-row: 2 / 3;
}
aside#ads {
grid-column: -2 / -1;
grid-row: 2 / 3;
}
footer {
grid-column: 1 / -1;
grid-row: -2 / -1;
}
.child:first-child {
background: #FF0000AA;
/* 这是用四条边来描述一个矩形的位置的方式。*/
grid-column: 1 / 3; /* column 的范围从第一条 column 轨道到第三条 column 轨道 */
grid-row: 1 / 2; /* row 的范围从第一条 row 轨道到第二条 row 轨道 */
/* 也可以用对角点坐标的方式来等价的描述,等价于用对角点的描述方式:
用 (1, 1) 与 (3, 2) 这对对角坐标描述的位置。只不过,要把这种
坐标的记号方式,也换成下面这种斜线分割的形式。
*/
/* grid-are: 1 / 1 / 3 / 2; */
}
在 Chrome 运行上面的例子,然后打开 Chrome 的 Grid 调试工具 看看整个布局的样貌。
例子讲解:网格描述
前面的圣杯布局的例子定义了一个 3 x 3 的网格。参照 Grid 调试工具来具体解释下。
grid-template-rows: 50px 1fr 50px;
定义了 3 个水平方向的轨道(也称为行,Row):
- 第一个 Row 的高度要求是 50px;
- 第二个 Row 的高度是一个动态值,1fr 表示一份剩余空间,跟 Flex 布局里按比例分配剩余空间的含义类似;
- 第三个 Row 的高度也是 50px;
从而,有四条水平的 Grid Line,它们的编号及位置按顺序为:
- 起始位置;
- 距离起始位置 50px 的地方。因为第一个 Row 的高度是 50px;
- 距离第二条 Grid Line 1fr 高度的位置;
- 末尾位置;
当然了,为了方便描述,上述 Grid Line 位置的描述都是假定 “排水沟” 的宽度为0的情况下,实际本例子 grid-gap
是 10px
。
grid-template-columns: 1fr 960px 1fr;
定义了 3 个垂直方向的轨道(也称为列,Column):
- 第一个 Column 的宽度是1份剩余空间;
- 第二个 Column 的宽度是960px;
- 第三个 Column 的宽度是1份剩余空间;
从而,有四条垂直的 Grid Line,它们的编号及位置按顺序为:
- 起始位置;
- 动态位置,距离起始位置 1fr 宽度的位置;
- 距离第二条 Grid Line 960px 的位置
- 末尾位置,也是距离第三条 Grid Line 1fr 宽度的位置;
这是目前唯一一种描述网格的方式,即描述出 Rows 与 Columns,从而得到一个 Rows x Columns 的网格。那么这个网格就有 Rows + 1 条水平 Grid Line,以及 Columns + 1 条垂直 Grid Line。
描述 Tracks(也就是 Rows 与 Columns)的方式则有许多种:
- 长度单位,例如上面的
50px
、960px
。 - 剩余空间比例分配单位,例如上面的
1fr
。可以试着改改上面的代码,看如何让Ads
咱有剩余空间的 2/3。 - repeat 函数,例如:
grid-tempalte-columns: repeat(3, 1fr) 10px;
相当于grid-template-columns: 1fr 1fr 1fr 10px;
。 - 还有一些其它复杂一些的工具函数、关键字。后续结合例子介绍
例子讲解:子元素定位
画好网格之后,就需要把子元素(Grid Items)放在网格上。圣杯布局的例子中,画了一个 3 x 3 的网格,然后对这些 Grid Items 进行一一定位。
header
:Header 占用网格中第一行整行。所以,他的列跨度是从列的第一个网格线到列的最后一个网格线,即 grid-column: 1 / 4
,而行跨度是从行的第一个网格线,到行的第二个网格线,即 grid-row: 1 / 2
。这种方式就是在把这个矩形的四条边的位置都描述出来。从 Chrome 的 Debug 工具可以看出,每个网格线有两个编号,一个正数,一个负数。负数的表达方式表示“倒数第几个”。所以,这个 grid-column 也可以写成 1 / -1
表示从第一条网格线到最后一条网格线。
footer
:圣杯布局中 Footer 与 Header 定位类似,它需要占用网格中最后一行的整行。这里用负数就很好描述了,列的占用前面讲过了,可以是 grid-column: 1 / 4
也可以是 grid-column: 1 / -1
,显然后一个比较好表达。那么行的占用也很好表达,最后一行就是最后两条网格线构成的,所以是:grid-row: -2 / -1
。
其它的元素定位就没有什么好讲解的了,我把这种描述称为“描边表达式“。这里另外描述一种定位方式,用属性 grid-area
描述。这个属性可以取一个字符串,这个表示网格 ID,这里讨论。它的另一个取值跟 grid-column
与 grid-row
类似,所以 grid-area
也可以视为它们两个属性的简写。然而,其“顺序”却与 grid-column
跟 grid-row
有点出入,我把这种表达方式称为“对角坐标表达式“。
aside#toc
:这个元素的定位表达式是 grid-column: 1 / 2;
跟 grid-row: 2 / 3;
。我们结合 Chrome 的 Debug 工具,看看整个 Grid 网格线编号,以及这个元素所处的位置。网格线是一个直角坐标系,CSS的定位坐标系跟数学题里经常出现的直角坐标系方向不一样。看看这个网格,“Table Of Content“ 这个矩形的左上角的点坐标是多少?从 Debug 工具中可以很快看出是 (1, 2)
,水平方向为 x 轴(column 轴)垂直方向是 y 轴(row 轴)。同理,很容其右下角的点坐标是 (2, 3)
。那么 grid-area
就是把这两个点坐标写上去,就像这样:grid-area: (1, 2) (2 3)
,当然了这样写并不合法,要把括号跟逗号都去掉,用 /
分割,也就是 grid-area: 1 / 2 / 2 / 3
。