概述
一般来说设计工具中都有网格、参考线等工具来辅助一些对齐,排布功能,如 photoshop 和 sketch 等。
在没有 CSS 对应的网格功能之前,一般会使用 float 或 flexbox 等技术来模拟这种网格系统,比如960s,bootstrap 的网格系统。虽然模拟出来的网格系统在一定程度上可以一些解决问题,但是在面临一些如 Win10 UI 这种还是有点力所不及,如下图的布局:
既然有了使用场景的需求,肯定就会有新的标准技术出现,这就是这里要说的 CSS Grid Layout。其主要有以下优势:
- 为布局而生的二维网格布局系统。
- 功能强大。Win10 UI、响应式布局改变、二维列表等都是它的菜。
- 对比 flexbox 来说:flexbox 为单维、内容优先;而 grid 为二维,布局优先。如下图,图一为 flexbox,图二为 grid
PS:虽然 flexbox 也可以折行,视觉上看起来形成的也是二维的,但是它并不能进行跨行跨列的操作,所以 flexbox 并不是二维的布局。
同 flexbox 一样,grid 布局也是由两层结构组成,父元素设置 display: grid
形成 grid container,直接子元素就此成为 grid items,然后就可以通过给父子元素设置一些 CSS 属性,轻松实现一些网格布局了。
基本术语
既然是网格系统,那肯定是由线条组成的一个个格子,如下图水平红线与垂直蓝色相交就形成一个 2*3 的网格。
轴线(axis)
flexbox 有两个轴,分别是 main axis(主轴) 和 cross axis(十字轴)。而 grid 作为二维的布局系统,也有对应的两个轴,分别为:inline axis(行内轴,也叫 row axis)和 block axis (块轴,也叫 column axis)。理解这两个轴非常重要,因为分布对齐属性就是根据这两个轴来进行的。如下图:
注意:flexbox 的两个轴是会根据排列方向而改变的,但是 grid 的两个轴是不变的。
Grid container & grid items
给一个元素设置 display: grid
后,该元素就是 grid container 了,同时其直接子元素就是 grid items。
<div class="grid-container">
<div class="grid-item"></div>
<div class="grid-item"></div>
<div class="grid-item"></div>
<div class="grid-item"></div>
</div>
/* grid container */
.grid-container {
display: grid;
}
/* grid items */
.grid-item {}
Grid lines
构成网格系统的线条,就叫 grid lines,如下图:
Grid cell
而线条所围成的每一个格子,就叫 grid cell,如下图:
Grid track
整个一行或者一列,就叫 grid track(对应 table 的 row 或 column),如下图:
Grid area
由任意两条横向网格线和两条纵向网格线所组成的区域都是 grid area(可以对其命名,以方便使用),其可能由一个或多个 grid cell 组成,如下图:
Grid cell vs grid items
最后考虑到有些格子会合并,所以 grid items 可能是单个的 grid cell,也有可能是多个 cell 合并的结果,如下图 3 和 4 item 就是 cell 合并的结果:
PS:其实网格系统跟我们日常工作中的 excel 有点像,如下图:
牛刀小试
因为网格系统的属性特别多,功能也非常的强大,所以这里先直接选个实战来简单熟悉下(具体深入下一节再详细分析)。下面开始实现概述中的效果图:
第一步:搭建结构
先数出 items 的个数,这里为 11 个,如下图:
设计 HTML 结构如下:
<div class="grid">
<div class="item">1</div>
<div class="item">2</div>
<div class="item">3</div>
<div class="item">4</div>
<div class="item">5</div>
<div class="item">6</div>
<div class="item">7</div>
<div class="item">8</div>
<div class="item">9</div>
<div class="item">10</div>
<div class="item">11</div>
</div
第二步:设计网格
写好结构后,再根据要实现的效果图拆分格子。如下图,红色和灰色的线条就是 grid lines:
这样我们就得到一个 3 6 的网格,其中 grid cell 的大小为 140px 140px,间距为 20px。现在我们就可以使用 grid container 的 CSS 相关属性完成初步的网格布局,代码如下:
.grid {
display: grid; /* 定义网格布局 */
/* 定义3行6列 */
grid-template-rows: 140px 140px 140px; /* 每个值表示每行的高度 */
grid-template-columns: 140px 140px 140px 140px 140px 140px; /* 每个值表示每列的宽度 */
/* 定义item之间的间距为20px */
grid-gap: 20px;
}
.item{
background: #ccc;
}
下面对代码中的 grid-template-rows
、grid-template-columns
和 grid-gap
进行具体解释。
grid-template-rows & grid-template-columns
grid-template-rows
用来定义网格的行,每个值代表一行的高度,上面的 3 个 140px 就表示有三行,高度都是 140px。如下,定义四行,每行的高度分别为 140px、300px、200px、400px:
.grid {
/* 定义四行 */
grid-template-rows: 140px 300px 200px 400px;
}
同理,grid-template-columns
用来定义网格的列,每个值代表一列的宽度。
如遇到有规律的重复列宽或行高,还可以使用 grid 中新增的 repeat 函数来表示重复,如上面的 140px 的 3 * 6 网格:
.grid {
grid-template-rows: repeat(3, 140px);
grid-template-columns: repeat(6, 140px);
}
grid-gap
该属性是 grid-column-gap
(列之间的间距)和 grid-row-gap
(行之间的间距)的简写,其语法为:grid-gap: <grid-column-gap> <grid-row-gap>
。
.grid {
grid-gap: 15px 30px;
}
/* 等同于 */
.grid {
grid-column-gap: 15px;
grid-row-gap: 30px;
}
如只有一个值,则行列间距相等,如 gird-gap: 20px;
。
第三步:items 定位
上面网格实现的效果图如下:
看起来,这跟我们要实现的效果相差甚远,但其实我们只差最后一步:将 items 定位到对应的网格中即可。
简单来说,grid 中的每个 items 都可以借助四条线来决定定位。这四条线分别是两条横线和两条竖线,其形成的交叉区域就是定位的位置。默认的话,一个 item 占据一个 cell,而如果一个 item 占据多个 cell,就需要进行额外的定位处理,如下图:
如上图中,里面使用了 grid-area
来实现定位,其四个值以右斜线分割,分别表示行开始线、列开始线、行结束线、列结束线。除了使用 grid-area
之外,还可以使用 grid-column
来指定两条列线,grid-row
来指定两条行线。
grid-column & grid-row
grid-column
属性是 grid-column-start
和 grid-column-end
的简写,表示列线的起始与结束,其语法为:grid-column: <start-line> / <end-line>
;同样 grid-row
属性是 grid-row-start
和 grid-row-end
的简写,表示行线的起始与结束,其语法为:grid-row: <start-line> / <end-line>
。它们都属于 grid items 的属性,用来定义如何合并 grid cell。如上面的 grid-area: 1 / 3 / 3 / 5
就可以写成:
.item2 {
/* 行起始与结束 */
grid-row-start: 1;
grid-row-end: 3;
/* 列起始与结束 */
grid-column-start: 3;
grid-column-end: 5;
}
合并 cell
现在我们已经知道如何合并 cell 了,那么先把需要合并的 items 罗列出来,分别是1、2、5、7、9。如下图:
第一个 item 元素单元格占了两列,第一列和第二列,那么其垂直列开始于第一条 line,结束于第三条 line,同样第五个 item 元素也是如此:
.item:nth-child(1),
.item:nth-child(5) {
grid-column: 1 / 3; /* 起始于1,结束于3 */
}
而第二个 item 元素列和行都跨了两个,CSS 代码如下:
.item:nth-child(2) {
grid-column: 3 / 5; /* column起始于3,结束于5 */
grid-row: 1 / 3; /* row起始于1,结束于3 */
}
同样第七个 item 元素行跨了两个,第九个 item 元素列跨了两个,CSS 代码如下:
.item:nth-child(7) {
grid-column: 6;
grid-row: 2 / 4; /* row起始于2,结束于4 */
}
.item:nth-child(9) {
grid-column: 2 / 4; /* column起始于2,结束于4 */
}
最后效果图如下:
总结
一般来说,对于一个不怎么复杂的网格系统,无非是先设计出对应的网格系统,然后对 items 进行定位而已。涉及到的属性也无非是如何创建行和列,如何合并 cell,所以没那么复杂。当然这只是冰山一角而已,要玩转 grid,光这些还是远远不够的,下节将继续深入挖掘。