记Grid布局

228 阅读10分钟

前言

由于之前一直在开发b端的项目,基本都基于现有的组件库开发,手动写样式布局的情况很少,最近手里拿到了一个移动端的项目,正好进行下查缺补漏,希望也能对大家有一些小小滴帮助~

grid

网格布局Grid是目前最强大的Css布局方案,它与弹性布局Flex有一定的相似处,都可以指定所定义容器内的多个元素的位置,但Flex只能指定相应于轴线的位置,可以看作是一维布局,而Grid按照行和列把容器划分成了多个单元格,之后指定相应于单元格的位置,可以看作是二维布局,所以它更强大。我们通过display:grid或者display:inline-grid去设置网格布局。

设置为网格布局之后,容器子元素的 float 、 display:inline-block 、 display:table-cell 、 vertical-algin:center 和 column-* 等将会失效。

网格

就像我之前说的,网格是一组相交的水平线和垂直线组成的,可以想象成是网格的行和列,元素就放置在相应的位置上。

如图该容器就被分割成了9个单元格,以mn列为例,会产生m*n个单元格,而m行就有m+1条水平线,n列就有n+1条垂直线。

固定的位置和弹性的轨道的大小

我们可以使用px等固定的尺寸去创建网格,也可以使用百分比或者专门的单位fr(fraction,译为"片段")去弹性的创建网格,我写一个等价的例子。

html部分(后续例子同):

    <div class="container">
        <div class="item1">1</div>
        <div class="item2">2</div>
        <div class="item3">3</div>
        <div class="item4">4</div>
        <div class="item5">5</div>
        <div class="item6">6</div>
        <div class="item7">7</div>
        <div class="item8">8</div>
        <div class="item9">9</div>
    </div>

css部分:

    * {
        margin: 0;
        padding: 0;
    }
    .container {
        display: grid;
        grid-template-columns: repeat(3,150px);
        grid-template-rows: repeat(3,150px);
        font-size: 28px;
        width: 450px;
        height: 450px;
        border: 2px dotted #333333;
        margin: 100px auto;
    }
    .container {
        display: grid;
        grid-template-columns: repeat(3,33.3%);
        grid-template-rows: repeat(3,33.3%);
        font-size: 28px;
        width: 450px;
        height: 450px;
        border: 2px dotted #333333;
        margin: 100px auto;
    }
    .container {
        display: grid;
        grid-template-columns: repeat(3,1fr);
        grid-template-rows: repeat(3,1fr);
        font-size: 28px;
        width: 450px;
        height: 450px;
        border: 2px dotted #333333;
        margin: 100px auto;
    }
    .container > div {
       text-align: center;
       line-height: 150px;
    }
    .item1{
        background-color: #ef342a;
    }
    .item2 {
        background-color: #4a4a22;
    }
    .item3 {
        background-color: #a2d5dd;
    }
    .item4 {
        background-color: #c077c2;
    }
    .item5 {
        background-color: #d0e4a9;
    }
    .item6 {
        background-color: #ff8899;
    }
    .item7 {
        background-color: #00ffff;
    }
    .item8 {
        background-color: #e0e0ee;
    }
    .item9 {
        background-color: #f8d29d;
    }

效果:

三种方式都可以达到划分单元格位置和大小的目的,例子中包含grid-template-columns,grid-template-rows,repeat().

  • grid-template-rows | grid-template-columns

当我们用display:grid定义了网格容器,接下来就要通过行和列去划分网格,grid-template-rows定义了每一行的行高,grid-template-columns定义了每一列的列宽。

.item {
    display: grid;
    grid-template-rows: 100px 100px 100px;
    grid-template-columns: 100px 100px 100px;
}

上面就定义了一个三行三列的网格,行高和列宽为100px.当然也可以使用百分比和fr定义.我们还可以使用方括号,指定每一根网格线的名字.

.container {
  	display: grid;
  	grid-template-columns: [c1] 100px [c2] 100px [c3] auto [c4];
  	grid-template-rows: [r1] 100px [r2] 100px [r3] auto [r4];
}

还可以允许一根线有多个名字,[one two].

  • repeat()

当我们的网格轨道很多时,一个一个写难免会非常的麻烦,我们可以使用repeat()函数去重复部分轨道或者整个轨道列表,我们使用repeat()改写上边的代码。

.item {
    display: grid;
    grid-template-rows: repeat(3, 100px);
    grid-template-columns: repeat(3, 100px);
}

主要接受两个参数:第一个参数为重复的次数,第二个为所要重复的部分(可以是单个值,也可以是多个值)

  • auto-fill

该属性表示自动填充,当我们设置了行高和列宽之后,我们的容器大小比如是不固定的,我们可以设置auto-fill配合repeat()去填充.

.item {
  display: grid;
  grid-template-columns: repeat(auto-fill, 100px);
  grid-template-rows: repeat(3, 100px);
}

效果:

容器宽度未知,元素按100px填充。

  • fr

上边说的fr,表示的不仅是一个单位,如果两列的宽度为1fr,2fr,表示后者的宽度是前者的两倍。

    .container {
        display: grid;
        grid-template-columns: 1fr 2fr 3fr;
        font-size: 28px;
        width: 450px;
        height: 450px;
        border: 2px dotted #333333;
        margin: 100px auto;
    }

效果:

  • minmax()

该函数表示一个范围,比如minmax(100px, 1fr)就表示,不小于100px且不大于1fr.

  • auto

该属性我的理解是充满,剩多少都充满。

元素位置

我们可以使用行号、行名等去定义一个网格区域,去精准定位元素。相关的有grid-row-gapgrid-column-gapgrid-gap .

  • grid-row-gap | grid-column-gap | grid-gap

分别表示行和行之间的距离,列与列之间的距离,以及两个的简写。

.item {
	grid-gap: "grid-row-gap" "grid-column-gap";
}

  • grid-template-areas

该属性定义了area区域的概念,一个区域可能由多个单元格组成

.item {
	grid-template-areas: 'a b c'
    			     'd e f' 
                             'g h i';
}

上面划分了9个单元格,定义为从a~i的区域,与之相对应,我们可以多个单元格同一命名表示一片区域,如果我们不需要该区域我们也可以用.去表示,我举一个我们布局的实际例子:

html部分:

    <div class="container">
        <div class="item1">header</div>
        <div class="item2">sidebar</div>
        <div class="item3">main</div>
    </div>

css 部分:

    * {
        margin: 0;
        padding: 0;
    }
    .container {
        display: grid;
        grid-template-areas: 'header header header'
                             'sidebar main main'
                             'sidebar main main';
        font-size: 28px;
        width: 450px;
        height: 450px;
        border: 2px dotted #333333;
        margin: 100px auto;
    }
    .container > div {
       display: flex;
       align-items: center;
       justify-content: center;
    }
    .item1{
        background-color: #ef342a;
        grid-area: header;
    }
    .item2 {
        grid-area: sidebar;
        background-color: #4a4a22;
    }
    .item3 {
        grid-area: main;
        background-color: #a2d5dd;
    }

效果:

我们可以通过使用该属性方便的达到一个整体的布局。

  • grid-auto-flow

把容器划分为网格之后,元素默认的放置顺序是"先行后列",我们可以通过该属性去改变顺序.

.item {
	grid-auto-flow: 'row' | 'column''row dense' | 'column dense';
}

属性分别表示先行后列和先列后行。

row densecolumn dense分别表示比如某些元素被指定了特殊的位置,剩下的元素要怎么排列,并是尽可能密集的排列,不出现空格。

如图,2区域被指定,按列尽可能排列,3区域和4区域补充上空白区域。

  • grid-column-start | grid-column-end | grid-row-start | grid-row-end

上面提到了指定特殊的位置,我们可以通过以上四个属性去指定元素的四个边框,分别指定到哪根网格线,我实现下上左图的效果:

html部分:

    <div class="container">
        <div class="item1">1</div>
        <div class="item2">2</div>
        <div class="item3">3</div>
        <div class="item4">4</div>
    </div>

css部分:

    * {
        margin: 0;
        padding: 0;
    }
    .container {
        display: grid;
        grid-template-columns: repeat(3,150px);
        grid-template-rows: repeat(3,150px);
        grid-auto-flow: row;
        font-size: 28px;
        width: 450px;
        height: 450px;
        border: 2px dotted #333333;
        margin: 100px auto;
    }
    .container > div {
       text-align: center;
       line-height: 150px;
    }
    .item1{
        background-color: #ef342a;
    }
    .item2 {
        grid-column-start: 1;
        grid-column-end: 4;
        background-color: #4a4a22;
    }
    .item3 {
        background-color: #a2d5dd;
    }
    .item4 {
        background-color: #c077c2;
    }

效果:

我们除了可以用第几根网格线去指定,还可以用网格线的名字去指定,其中还有span关键字表示跨越多少个网格,上边的效果我们也可以通过这样设置css去改变:

    .item2 {
        grid-column-start: span 4;
        background-color: #4a4a22;
    }

达到的效果是一样的。

  • grid-column | grid-row

这两个属性表示以上四个属性的缩写,具体表示为:

 .item {
   grid-column: <start-line> / <end-line>;
   grid-row: <start-line> / <end-line>;
 }
  • grid-area

该属性指定项目放在哪个区域:

.item1 {
	grid-area: 'container';
}

之前布局的例子中提到了相应的用法,这里表示一号位于container区域。

创建额外的轨道包含元素

有些时候,我们指定为网格容器的区域大小固定,有一个元素被指定到了外部,此时我们的浏览器会自动的生成网格帮助我们去放置这些外部的元素,我们可以通过上面两个属性去给外部的元素设置行高和列宽.

  • grid-auto-rows | grid-auto-columns
    * {
        margin: 0;
        padding: 0;
    }
    .container {
        display: grid;
        grid-template-columns: repeat(3,1fr);
        grid-template-rows: repeat(3,1fr);
        grid-auto-rows: 100px;
        font-size: 28px;
        width: 450px;
        height: 450px;
        border: 2px dotted #333333;
        margin: 100px auto;
    }
    .container > div {
       text-align: center;
       line-height: 150px;
    }
    .item1{
        background-color: #ef342a;
    }
    .item2 {
        background-color: #4a4a22;
    }
    .item3 {
        background-color: #a2d5dd;
    }
    .item4 {
        background-color: #c077c2;
    }
    .item5 {
        background-color: #d0e4a9;
    }
    .item6 {
        background-color: #ff8899;
    }
    .item7 {
        background-color: #00ffff;
    }
    .item8 {
        grid-column: 2 / 3;
        grid-row: 4 / 5;
        background-color: #e0e0ee;
    }
    .item9 {
        grid-row: 5 / 6;
        background-color: #f8d29d;
    }

修改九宫格例子的代码,我们可以得到如下效果:

  • grid-template | grid

grid-template属性是grid-template-columnsgrid-template-rowsgrid-template-areas这三个属性的合并简写形式。grid属性是grid-template-rowsgrid-template-columnsgrid-template-areasgrid-auto-rowsgrid-auto-columnsgrid-auto-flow这六个属性的合并简写形式,这里不多介绍了,也不太推荐这样书写,代码会让人看不懂...

对齐控制

网格内的元素可以通过设置一些属性达到对齐的效果.以及整个网格如何对齐.

  • justify-items | align-items | place-items

前两个属性,分别表示单元格内容的左中右和上中下的形式排列。后面是前两种的简写形式。

.item {
	justify-items: "start" | "end" | "center" | "stretch";
}

.item {
	align-items: "start" | "end" | "center" | "stretch";
}

.item {
	place-items: "align-items" "justify-items";
}

我们面试中常考的水平垂直居中我们可以通过设置该属性实现:

.item {
	place-items: center;
}

  • justify-content | align-content | place-content 前两个属性,分别表示整个内容的左中右和上中下的形式排列。后面是前两种的简写形式。
.item {
	justify-content: "start" | "end" | "center" | "space-around" | "space-between" | "space-evenly";
}

.item {
	algin-content: "start" | "end" | "center" | "space-around" | "space-between" | "space-evenly";
}

.item {
	place-content: "algin-content" "justify-content";
}

我们也可以通过设置这个属性达到大容器内的div元素水平垂直居中place-content: center;

  • justify-self | algin-self | place-self

我们可以用justify-items来对比,用法完全一致,就是justify-self是作用于单个元素。

.item {
  justify-self: start | end | center | stretch;
}

justify-self

.item {
  align-self: start | end | center | stretch;
}

 align-self

控制重叠内容

当区域部分的重叠后,我们可以通过设置cssz-index属性去控制重叠区域的优先级。

浏览器支持

flex对比

当我们大量绘制,或者内容更改的时候,我们的页面是不稳定的,我们的flex是一种弹性布局,一些情况下会造成不可避免的重排,我们如果提前按宽高划分好我们的网格后,我们的grid可以避免布局重排的问题,但目前的布局我们的flex合理使用应该是都可以解决的,我们不能否认它的强大,flex在响应式布局中是很关键的,它是内容驱动型的布局,强大的一维布局能力,我们不需要知道内容,可以直接设定,内容不足是如何分配空间的等等操作,grid的优势在于他是二维布局,更多的是未来叭~

写在后面

grid布局在本人实际操作中用到的也不多,也是相当于一次学习的整理,如果有不对或者缺少的基础部分大家可以指出,有什么意见也可以提出来,谢谢呀~

参考资料

Grid MDN

CSS Grid 网格布局教程 阮一峰