Loading...
墨滴

fengxiaodong

2021/10/06  阅读:59  主题:默认主题

深入浅出之grid网格布局

一、简介

Grid 网格布局 (下面都简称为 Grid 布局),是一个基于栅格的二维布局系统,旨在彻底改变基于网格用户界面的设计。CSS 一直以来并没有把布局做的足够好。刚开始,我们使用 table,后来是 floatpositioninline-block,这些本质上是一些 hacks 而且许多重要功能尚未解决(例如垂直居中)。虽然flex 弹性布局可以做到这些,但是 flex 布局实际上是一维布局,而 Grid 布局是二维的,它远比 flex 布局要强大,以下是 Grid 布局的浏览器兼容性,支持Chrome57+Edge16+Firefox52+Safari10.1+

二、容器和项目

采用网格布局的区域,称为"容器"(container)。容器内部采用网格定位的子元素,称为"项目"(item)。

三、行、列、单元格、 区域、网格线

容器里面的水平区域称为"行"(row),垂直区域称为"列"(column)。行和列的交叉区域,称为"单元格"(cell)。正常情况下,n行和m列会产生n x m个单元格。比如,例如:3 行 3 列会产生 9 个单元格。一定数量的单元格可以组成"区域"(area),区域必须是正方形/长方形的,不可以是其他形状。

划分网格的线,称为"网格线"(grid line)。水平网格线划分出行,垂直网格线划分出列。正常情况下,n行有n + 1根水平网格线,m列有m + 1根垂直网格线,比如三行就有四根水平网格线。

四、 容器属性

4.1 display: grid, display: grid: inline-grid

display: grid指定一个容器采用网格布局

默认情况下,网格都是块级元素,使用display: inline-grid可以将网格设置为行内元素

4.2 grid-template-columns,grid-template-rows

grid-template-columns属性定义每一列的列宽,grid-template-rows属性定义每一行的行高

grid-template-columns<rows>: length | percent | auto | fr

定义一个三行三列的网格,列宽和行高都是100px,使用了网格布局,基本无须单独控制子项的宽高了

.mian {
  display: grid;
  grid-template-columns100px 100px 100px;
  grid-template-rows100px 100px 100px;
}

.main div {
  background: pink;
}

除了使用绝对单位,也可以使用百分比

.main {
  display: grid;
  grid-template-columns33.33% 33.33% 33.33%;
  grid-template-rows33.33% 33.33% 33.33%;
}

也可以使用auto,子项会自适应父容器剩余大小去撑开

.main {
  display: grid;
  grid-template-columns100px 50px auto;
  grid-template-rows100px auto 50px;
}

最后可以使用推荐的关键字fr,可以理解为比例值,将行与列都分成 3 份

.main {
  display: grid;
  grid-template-columns1fr 1fr 1fr;
  grid-template-rows1fr 1fr 1fr;
}

将第二行与第二列都划分成 2 份

.main {
  display: grid;
    grid-template-columns1fr 2fr 1fr;
    grid-template-rows1fr 2fr 1fr;
}
4.3 grid-template-areas

网格布局允许指定"区域"(area),一个区域由单个或多个单元格组成。grid-template-areas属性用于定义区域

.main {
  display: grid;
  grid-template-columns100px 100px 100px;
  grid-template-rows100px 100px 100px;
  grid-template-areas'a b c'
    'd e f'
    'g h i';
}

可以将多个单元格合并成一个区域,其中grid-area后面会介绍,这里先不作深究

.main {
  display: grid;
  grid-template-columns100px 100px 100px;
  grid-template-rows100px 100px 100px;
  grid-template-areas'a a b'
    'a a b'
    'c c c';
}

.main div:nth-child(1) {
  grid-area: a;
  background: skyblue;
}
.main {
   display: grid;
    grid-template-columns100px 100px 100px;
   grid-template-rows100px 100px 100px;
  grid-template-areas'a a b'
                         'a a b'
                         'c c c';
}
.main div:nth-child(1) {
    grid-area: a;
}
.main div:nth-child(2) {
    grid-area: b;
}
.main div:nth-child(3) {
    grid-area: c;
}
4.4 grid-template

grid-template属性是grid-template-columnsgrid-template-rowsgrid-template-areas这三个属性的合并简写形式。但是不建议合并在一起写,所以这里就不作案例展示,毕竟这些属性本身比较难记。可以看 MDN 上关于 grid-template 相关的介绍

4.5 grid-row-gap,grid-column-gap

grid-row-gap属性设置行与行的间隔(行间距),grid-column-gap属性设置列与列的间隔(列间距)。需要注意的是,CSS Grid Layout 起初是用 grid-row-gap 属性来定义的,目前逐渐被 row-gap 替代。因为其他布局方式也可以使用 gap 属性,例如flex弹性布局。但是,为了兼容那些不支持 row-gap 属性的浏览器,你需要像上面的例子一样使用带有前缀的属性。详情看 MDN 关于row-gap的介绍

grid-row<column>-gaplength
.main{
    display: grid;
    grid-template-columns100px 100px 100px;
    grid-template-rows100px 100px 100px;
    row-gap20px;
    column-gap20px;
}

可以看到行与列之间多了 20px 的间距

4.6 grid-gap

grid-gap属性是grid-column-gapgrid-row-gap的合并简写形式,现在已经逐渐被 gap 替代

grid-gap: <grid-row-gap> <grid-column-gap>;
.main{    display: grid;    grid-template-columns100px 100px 100px;    grid-template-rows100px 100px 100px;    gap50px;}
4.7 justify-items,align-items

justify-items属性设置单元格内容的水平位置,align-items属性设置单元格内容的垂直位置,默认值都是stretch

justify-items<align-items>: start | end | center | stretch;

首先我们先定义一个 3 行 3 列的网格

.main {
  display: grid;
  grid-template-columns100px 100px 100px;
  grid-template-rows100px 100px 100px;
}

.main div {
  background: pink;
}

为什么在没有给定子项宽高的情况下,仍然可以铺满整个父容器?原因是因为水平与垂直都是默认拉升的(stretch),也就是:

.main div {  justify-items: stretch;  align-items: stretch;}

如果我们给子项设置宽高:

.main div { width50pxheight50px;}

虽然子项的宽高发生了变化,但是实际上网格的大小仍然没有变,用调试工具就可以看出来:

这个情况下,可以使用justify-itemsalign-items来对齐子项的位置

行与列设置居中对齐

.main{    display: grid;    grid-template-columns100px 100px 100px;    grid-template-rows100px 100px 100px;    justify-items: center;    align-items: center;}.main div{    width50px;    height50px;    background:pink;}

行与列设置尾部对齐

.main{
    justify-items: end;
    align-items: end;
}
4.8 place-items

place-items属性是align-items属性和justify-items属性的合并简写形式,注意的是,垂直对齐是在前,水平对齐是在后。如果只写一个,则默认垂直与水平是一致。

place-items: <align-items> <justify-items>;
.main{
    display: grid;
    grid-template-columns100px 100px 100px;
    grid-template-rows100px 100px 100px;
    place-items: center;
}
.main div{
    width50px;
    height50px;
    background:pink;
}
4.9 justify-content , align-content

justify-content属性是整个内容区域在容器里面的水平位置,align-content属性是整个内容区域的垂直位置。默认都是start

justify-content<align-content>: start | end | center | stretch | space-around | space-between | space-evenly;

首先定义一个 3 行 3 列的网格布局

.main{
    width:500px;
    height:500px;
    display: grid;
    grid-template-columns100px 100px 100px;
    grid-template-rows100px 100px 100px;
}
.main div {
  background: pink;
}

向结束位置对齐

.main{   justify-content: end;   align-content: end;}

居中对齐

.main{
   justify-content: center;
   align-content: center;
}

两端对齐

.main{   justify-content: space-between;   align-content: space-between;}

子项与子项之间间隔相等

.main{
   justify-content: space-around;
   align-content: space-around;
}

子项与子项之间间隔相等,子项与容器边框之间间隔相等

.main{   justify-content: space-evenly;   align-content: space-evenly;}

其他情况不在此继续展示,可自行尝试

4.10 place-content

place-content属性是align-content属性和justify-content属性的合并简写形式。和place-items一样,垂直在前,水平在后。如果只写一个,则默认垂直与水平是一致。

place-content: <align-content> <justify-content>
.main{    place-content: center;}
4.11 justify-items,align-items 与 justify-content,align-content 的注意点

这两对属性和flex弹性布局很相似,所以经常搞混,这里特别要说明的是,与flex布局不一样的地方是,这两对属性都是作用在父容器上,而flex布局justify-contentalign-items属性是作用在父容器上的,但是没有justify-itemsalign-content这两个属性。一般在Grid布局中,justify-itemsalign-items是一起使用,justify-contentalign-content是一起使用

那么什么时候使用justify-itemsalign-items,什么时候使用justify-contentalign-content呢?

可以记住一个技巧:

(1)当子项的大小低于单元格的大小的时候,使用justify-itemsalign-items

还记得前面说的吗,子项的大小是可能会比单元格小的,例如单元格大小是100*100,而子项大小只有50*50。这种情况使用justify-itemsalign-items

(2)当容器大小大于单元格大小时,使用justify-contentalign-content
4.12 显示网格与隐式网格

正常情况下,我们定义了 例如三行三列的网格,总共有9个元素,那这9个元素正好布满整个容器,这9个子项都是显示网格

.main{
    display: grid;
    grid-template-columns100px 100px 100px;
    grid-template-rows100px 100px 100px;
}
.main div {
  background: pink;
}

如果有超过9个子项呢?比如,数字10,数字11,数字12.....??这些多出来的网格就是隐式网格,这些隐式网格默认都是按照rows的方向进行排列,高度会自动拉伸铺满容器

4.13 grid-auto-flow

明白了什么是显示网格隐式网格,就可以看一下grid-auto-flow。划分网格以后,容器的子元素会按照顺序,自动放置在每一个网格。默认的放置顺序是"先行后列",即先填满第一行,再开始放入第二行。这就解释了,上面的隐式网格为什么会在789的下面,明明父容器右侧还有空白位置,为什么无法铺满,这是因为默认设置了"先行后列",即grid-auto-flow: rows,这个属性会决定网格的排列顺序

grid-auto-flowrows |  column | ros dense | column dense;

将顺序设置为先列后行,即grid-auto-flow: column

.main{
    width:500px;
    height:500px;
    display: grid;
    grid-template-columns100px 100px 100px;
    grid-template-rows100px 100px 100px;
    grid-auto-flow: column;
}
.main div {
  background: pink;
}

如果将网格1和网格2各占据两个单元格,然后在默认grid-auto-flow: row情况下,会产生下面这样的布局

上图中,网格1和网格2后面的位置都空了出来,因为网格3的排序是默认跟着网格2的,设置grid-auto-flow: row dense ,可以将空出的位置铺满

.main{
    width:300px;
    height:400px;
    display: grid;
    grid-template-columns100px 100px 100px;
    grid-template-rows100px 100px 100px;
    grid-auto-flow: row dense;
}

如果是"先列后行"的情况

.main{
    width:500px;
    height:300px;
    display: grid;
    grid-template-columns100px 100px 100px;
    grid-template-rows100px 100px 100px;
    grid-auto-flow: column
}
4.14 grid-auto-rows,grid-auto-columns

如果是先行后列的顺序,隐式网格的列宽默认和显示网格一样,行高则是拉伸铺满,如果是先列后行的顺序,隐式网格的行高默认和显示网格一样。难道隐式网格的高度或者宽度只能拉升吗?当然不是,可以使用grid-auto-rowsgrid-auto-columns控制隐式网格的行高和列宽

先行后列的顺序下,设置隐式网格行高为50px

.main{
    width:500px;
    height:300px;
    display: grid;
    grid-template-columns100px 100px 100px;
    grid-template-rows100px 100px 100px;
    grid-auto-flow: rows;
    grid-auto-rows50px;
}

先列后行的顺序下,设置隐式网格列高为50px

.main{    width:500px;    height:300px;    display: grid;    grid-template-columns100px 100px 100px;    grid-template-rows100px 100px 100px;    grid-auto-flow: column;    grid-auto-columns50px;}

隐式网格的行高也设置成100px,这样就和显示网格展示的大小一样了

.main{
    width:500px;
    height:400px;
    background:skyblue;
    display: grid;
    grid-template-columns100px 100px 100px;
    grid-template-rows100px 100px 100px;
    grid-auto-rows100px;
}
4.15 repeat()方法与 auto-fill 关键字

我们在定义网格的时候,写grid-template-columnsgrid-template-rows都是写多个重复值,比如grid-template-columns: 100px 100px 100px,简单的网格可以这样定义,但是如果网格很多的时候,难道要这样写吗?grid-template-columns: 100px 100px 100px 100px 100px ......,这样很麻烦,可以使用repeat()去简化重复的值

repeat(numberlength | percent | fr)

定义一个 5 行 4 列的网格

.main{
    display: grid;
    grid-template-columnsrepeat(5100px);
    grid-template-rowsrepeat(4100px);
}

也可以具体的数值和repeat()函数一起使用

.main{
    display: grid;
    grid-template-columns200px repeat(2100px);
    grid-template-rows200px repeat(2100px);
}

还有一种情况,容器的宽度有时候可以容纳更多的网格,但是由于定义了列数量或者行的数量比较少时

.main{
    width500px;
    background:skyblue;
    display: grid;
    grid-template-columnsrepeat(3100px);
    grid-template-rowsrepeat(3100px);
}

明明网格4是可以继续在网格3的后面继续排列的,为什么需要换行呢,仅仅是因为使用了repeat(3, 100px)定义三列,有什么办法,在单元格的大小是固定的,但是容器的大小不确定的情况下,使得每一行(或每一列)容纳尽可能多的单元格,这时可以使用auto-fill关键字表示自动填充

.main{
    width500px;
    background:skyblue;
    display: grid;
    grid-template-columnsrepeat(auto-fill, 100px);
    grid-template-rowsrepeat(3100px);
}
4.16 minmax()方法

minmax()函数产生一个长度范围,表示长度就在这个范围之中。它接受两个参数,分别为最小值和最大值。

minmax(minmax)

定义一个列宽和行高不小于100px,不大于200px的网格

.main{
    display: grid;
    grid-template-columnsminmax(100px200pxminmax(100px200pxminmax(100px200px);
    grid-template-rowsminmax(100px200pxminmax(100px200pxminmax(100px200px);
}

minmax()方法可以和repeat()方法一起使用

.main{
    display: grid;
    grid-template-columnsrepeat(3, minmax(100px150px));
    grid-template-rows100px 100px 100px;
}

五、项目属性

5.1 grid-area

首先定义一个三行三列的网格

默认情况下,网格数字都是按顺序排列的,有什么办法可以指定位置摆放呢?grid-area属性就可以指定项目放在哪一个区域,但前提是需要先用grid-template-areas属性指定了区域

网格1放到区域e

.main{
    display: grid;
    grid-template-columnsrepeat(3100px);
    grid-template-rowsrepeat(3100px);
    grid-template-areas'a b c'
                         'd e f'
                         'g h i';
}
.main div:nth-child(1) {
    grid-area: e;
    background: chartreuse;
}
5.2 grid-column-start , grid-column-end 和 grid-row-start , grid-row-end 与 span 关键字

这两对属性表示 grid 子项占据的区域的起始和终止位置,具体方法就是指定项目的四个边框,分别定位在哪根网格线

  • grid-column-start属性:左边框所在的垂直网格线
  • grid-column-end属性:右边框所在的垂直网格线
  • grid-row-start属性:上边框所在的水平网格线
  • grid-row-end属性:下边框所在的水平网格线

如何查看所在的网格线?可以利用浏览器的调试工具,这样就可以展示网格线的序号了

grid-column-startgrid-column-end

.main{
    background:skyblue;
    display: grid;
    grid-template-columnsrepeat(3100px);
   grid-template-rowsrepeat(3100px);
}
.main div{
    background:pink;
    grid-column-start1;
    grid-column-end3;
}

grid-row-startgrid-row-end

.main{
    background:skyblue;
    display: grid;
    grid-template-columnsrepeat(3100px);
   grid-template-rowsrepeat(3100px);
}
.main div{
    background:pink;
    grid-row-start2;
    grid-row-end3;
}

两对属性一起使用

.main div {
  grid-column-start2;
    grid-column-end3;
    grid-row-start2;
    grid-row-end3;
}

除了使用这种方式外,还可以直接命名网格线,效果也是一样的,使用调试工具可以查看网格线的命名

.main{
    background:skyblue;
    display: grid;
    grid-template-columns: [col1] 100px [col2] 100px [col3] 100px [col4];
   grid-template-rows: [row1] 100px [row2] 100px [row3] 100px [row4];
}
.main div {
  grid-column-start: col2;
    grid-column-end: col3;
    grid-row-start: row2;
    grid-row-end: row3;
}

这两对属性还可以使用span关键字,表示"跨越",即左右边框(上下边框)之间跨越多少个网格

grid-column<row>-startspan number;
.main div {
  grid-column-start: span 2;
}
.main div {
  grid-column-start2;
  grid-column-end: span 2;
}
5.3 grid-row,gird-column

grid-column属性是grid-column-startgrid-column-end的合并简写形式,grid-row属性是grid-row-start属性和grid-row-end的合并简写形式

grid-column: <start-line> / <end-line>;
grid-row: <start-line> / <end-line>;
.item-1 {
  grid-column1 / 3;
  grid-row1 / 2;
}
/* 等同于 */
.item-1 {
  grid-column-start1;
  grid-column-end3;
  grid-row-start1;
  grid-row-end2;
}

这两个属性之中,也可以使用span关键字,表示跨越多少个网格

.item-1 {
  grid-column1 / span 2;
  grid-row1 / span 2;
}
/* 等同于 */
.item-1 {
  grid-column1 / 3;
  grid-row1 / 3;
}

斜杠以及后面的部分可以省略,默认跨越一个网格

.item-1 {
  grid-column1;
  grid-row1;
}

grid-area也可以实现同样的效果

grid-areagrid-row-start / grid-column-start / grid-row-end / grid-column-end;
.main{
    background:skyblue;
    display: grid;
    grid-template-columnsrepeat(3100px);
    grid-template-rowsrepeat(3100px);
}
.main div{
    background:pink;
    grid-area2 / 2 / 3 / 3;
}
5.4 justify-self,align-self

justify-self属性设置单元格内容的水平位置(左中右),跟justify-items属性的用法完全一致,但只作用于单个项目

align-self属性设置单元格内容的垂直位置(上中下),跟align-items属性的用法完全一致,也是只作用于单个项目

justify-selfstart | end | center | stretch;
align-selfstart | end | center | stretch;
  • start:对齐单元格的起始边缘
  • end:对齐单元格的结束边缘
  • center:单元格内部居中
  • stretch:拉伸,占满单元格的整个宽度(默认值)
.main{
    background:skyblue;
    display: grid;
    grid-template-columnsrepeat(3100px);
    grid-template-rowsrepeat(3100px);
}
.main div{
  width50px;
    height50px;
    background:pink;
    grid-area2 / 2 / 3 / 3;
    justify-self: end;
    align-self: end;
}
5.5 place-self

place-self属性是align-self属性和justify-self属性的合并简写形式。如果省略第二个值,place-self属性会认为这两个值相等

place-self: <align-self> <justify-self>;
.main{
    background:skyblue;
    display: grid;
    grid-template-columnsrepeat(3100px);
    grid-template-rowsrepeat(3100px);
}
.main div{
  width50px;
    height50px;
    background:pink;
    grid-area2 / 2 / 3 / 3;
    place-self: center;
}

六、布局案例

6.1 栅格布局

利用grid布局可以轻松实现栅格布局

.row {
  background: skyblue;
  display: grid;
  grid-template-columnsrepeat(121fr);
  grid-template-rows50px;
  grid-auto-rows50px;
}

.row div {
  background: pink;
  border1px black solid;
}

.row .col-1 {
  grid-area: auto/auto/auto/span 1;
}

.row .col-2 {
  grid-area: auto/auto/auto/span 2;
}

.row .col-3 {
  grid-area: auto/auto/auto/span 3;
}

.row .col-4 {
  grid-area: auto/auto/auto/span 4;
}

.row .col-5 {
  grid-area: auto/auto/auto/span 5;
}

.row .col-6 {
  grid-area: auto/auto/auto/span 6;
}

.row .col-7 {
  grid-area: auto/auto/auto/span 7;
}

.row .col-8 {
  grid-area: auto/auto/auto/span 8;
}

.row .col-9 {
  grid-area: auto/auto/auto/span 9;
}

.row .col-10 {
  grid-area: auto/auto/auto/span 10;
}

.row .col-11 {
  grid-area: auto/auto/auto/span 11;
}

.row .col-12 {
  grid-area: auto/auto/auto/span 12;
}
<div class="row">
  <div class="col-6">1</div>
  <div class="col-3">2</div>
  <div class="col-4">3</div>
  <div class="col-5">4</div>
</div>
6.2 不规则子项排列

如果要实现这样的效果,使用grid布局也是轻而易举实现的

.main {
  width308px;
  margin20px auto;
}

.main-list {
  height352px;
  margin0 14px;
  display: grid;
  grid-template-columnsrepeat(31fr);
  grid-template-rowsrepeat(41fr);
  grid-template-areas:
    "a1 a3 a3"
    "a2 a3 a3"
    "a4 a4 a5"
    "a6 a7 a7";
  gap8px;
}

.main-list div:nth-of-type(1) {
  grid-area: a1;
}

.main-list div:nth-of-type(2) {
  grid-area: a2;
}

.main-list div:nth-of-type(3) {
  grid-area: a3;
}

.main-list div:nth-of-type(4) {
  grid-area: a4;
}

.main-list div:nth-of-type(5) {
  grid-area: a5;
}

.main-list div:nth-of-type(6) {
  grid-area: a6;
}

.main-list div:nth-of-type(7) {
  grid-area: a7;
}

.main-list a {
  width100%;
  height100%;
  display: block;
  line-height30px;
}

.main-list h3 {
  text-align: right;
  margin-right4px;
}

.main-list p {
  text-align: center;
}

.theme1 {
  background: pink;
}

.theme2 {
  background: skyblue;
}

.theme3 {
  background: orange;
}
<div class="main">
  <div class="main-list">
    <div class="theme1"></div>
    <div class="theme2"></div>
    <div class="theme1"></div>
    <div class="theme1"></div>
    <div class="theme1"></div>
    <div class="theme3"></div>
    <div class="theme3"></div>
  </div>
</div>
6.3 更多布局案例

Grid by Example

七、总结

grid布局flex弹性布局一样,都是当下最流行的 CSS 布局方案之一。它的优点是可以实现多行多列的布局,属于二维布局,基本可以满足任何的布局页面。

优点:

  • 固定和灵活的轨道尺寸
  • 可以使用行号、名称或通过定位网格区域将项目放置在网格上的精确位置
  • 可以将多个项目放入网格单元格或区域中,它们可以彼此部分重叠

缺点:

  • 浏览器兼容性较差

  • 学习成本较高

grid布局可以说是目前最强大的 CSS 布局方案,在实际开发过程中,往往grid布局flex布局一起结合使用。

八、参考

fengxiaodong

2021/10/06  阅读:59  主题:默认主题

作者介绍

fengxiaodong