..

Grid 布局

概念

Grid 布局即为二维布局,这个应该是目前 Web 布局方式的”顶配“了。它的描述方式是:

  1. 创建一个二维网格。用一些水平、垂直的轨道(Tracks)画出一个二维网格。
  2. 画好网格之后,内部的子元素就可以进行定义自己要处于的什么位置。这就好比在一个直角坐标系上,要画一个矩形一样。描述这个矩形的位置有很多种方式。比如,我们只需要知道这个矩形两个对角点的坐标即可;或者我们也可以通过指明这个矩形四条边的位置来描述这个矩形。

例子:圣杯布局

我们运行一个典型的圣杯布局的例子,先有些感想认知。

<!-- 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):

  1. 第一个 Row 的高度要求是 50px;
  2. 第二个 Row 的高度是一个动态值,1fr 表示一份剩余空间,跟 Flex 布局里按比例分配剩余空间的含义类似;
  3. 第三个 Row 的高度也是 50px;

从而,有四条水平的 Grid Line,它们的编号及位置按顺序为:

  1. 起始位置;
  2. 距离起始位置 50px 的地方。因为第一个 Row 的高度是 50px;
  3. 距离第二条 Grid Line 1fr 高度的位置;
  4. 末尾位置;

当然了,为了方便描述,上述 Grid Line 位置的描述都是假定 “排水沟” 的宽度为0的情况下,实际本例子 grid-gap10px

grid-template-columns: 1fr 960px 1fr; 定义了 3 个垂直方向的轨道(也称为列,Column):

  1. 第一个 Column 的宽度是1份剩余空间;
  2. 第二个 Column 的宽度是960px;
  3. 第三个 Column 的宽度是1份剩余空间;

从而,有四条垂直的 Grid Line,它们的编号及位置按顺序为:

  1. 起始位置;
  2. 动态位置,距离起始位置 1fr 宽度的位置;
  3. 距离第二条 Grid Line 960px 的位置
  4. 末尾位置,也是距离第三条 Grid Line 1fr 宽度的位置;

这是目前唯一一种描述网格的方式,即描述出 Rows 与 Columns,从而得到一个 Rows x Columns 的网格。那么这个网格就有 Rows + 1 条水平 Grid Line,以及 Columns + 1 条垂直 Grid Line。

描述 Tracks(也就是 Rows 与 Columns)的方式则有许多种:

  1. 长度单位,例如上面的 50px960px
  2. 剩余空间比例分配单位,例如上面的 1fr。可以试着改改上面的代码,看如何让 Ads 咱有剩余空间的 2/3。
  3. repeat 函数,例如:grid-tempalte-columns: repeat(3, 1fr) 10px; 相当于 grid-template-columns: 1fr 1fr 1fr 10px;
  4. 还有一些其它复杂一些的工具函数、关键字。后续结合例子介绍

例子讲解:子元素定位

画好网格之后,就需要把子元素(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-columngrid-row 类似,所以 grid-area 也可以视为它们两个属性的简写。然而,其“顺序”却与 grid-columngrid-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