FrankKai / FrankKai.github.io

FE blog

Home Page:https://frankkai.github.io/

Geek Repo:Geek Repo

Github PK Tool:Github PK Tool

CSS进阶之Grid布局

FrankKai opened this issue · comments

在css-tricks的A Complete Guide to Flexbox一文中,有这样一句话:

Flexbox layout is most appropriate to the components of an application, and small-scale layouts, while the Grid layout is intended for larger scale layouts.

  • flex布局适用于应用组件、小型布局,微观布局
  • grid布局适用于更大型的布局,宏观布局

我一直以来觉得flex布局已经够用了,因此一直没有学习grid布局。
但是看到这句话,让我对grid产生了一些兴趣,到底grid是不是如作者所说:“grid适用于大型布局”,暂时先画一个问号“?”

当这篇博文结束时,相信对这个问号会有一个好的答案。

  • 初识grid布局
  • grid五大概念
  • Grid container、Grid Tracks(fr、repeat()、显隐式grid、minmax)、Grid lines、Grid cells、Grid areas、Gutters
  • 嵌套grid布局
  • grid实现覆盖式的布局并通过z-index控制Grid层级、控制Grid顺序

初识grid布局

  • CSS的grid布局,通过定义HTML元素的size,position和layer的关系,可以将一个页面划分成几个主要区域。
  • CSS的grid布局,使作者能够将元素对齐成为列和行。很多情况下的布局,通过grid要比table布局简单得多。例如,一个grid容器的子元素可以定位自身,因此可以实现重叠和层级关系,这跟CSS定位元素是类似的

最简grid布局示例

image

<div class="wrapper">
  <div class="one">One</div>
  <div class="two">Two</div>
  <div class="three">Three</div>
  <div class="four">Four</div>
  <div class="five">Five</div>
  <div class="six">Six</div>
</div>
.wrapper {
  display: grid;
  grid-template-columns: repeat(3, 1fr); // 3是重复次数,fr是flex系数
  grid-gap: 10px; // row-gap column-gap
  grid-auto-rows: minmax(100px, auto); // 行的最小最大宽度
}
.one {
  grid-column: 1 / 3; // grid-column-start, grid-column-end 从1开始到3结束(不包括3),可以理解成从1~3边区间内的面积。边的编号从1开始
  grid-row: 1; // grid-row-start, grid-row-end 1,auto区间内的面积。边的编号从1开始
}
.two { 
  grid-column: 2 / 4;
  grid-row: 1 / 3;
}
.three {
  grid-column: 1;
  grid-row: 2 / 5;
}
.four {
  grid-column: 3;
  grid-row: 3;
}
.five {
  grid-column: 2;
  grid-row: 4;
}
.six {
  grid-column: 3;
  grid-row: 4;
}

grid五大概念

  • 固定和灵活的轨道尺寸(fixed flexible)
  • 条目布局(item)
  • 为已有内容增加额外的轨道(hold content)
  • 对齐控制(alignment)
  • 重叠内容控制(overlap)

固定和灵活的轨道尺寸

  • 固定的轨道尺寸:可以理解为通过像素px创建网格,为网格设置了指定的大小
  • 灵活的轨道尺寸:可以理解为通过百分比或者是全新的fr单位创建的网格

条目布局(item)

  • 可以通过line number,name或者指定网格区域,将元素布局在网格的指定区域
  • grid有自己的算法:即使没有明确指定位置,在网格上也可以布局元素

为已有内容增加额外的轨道(hold content)

  • 可以在网格布局上定义明确的网格
  • 网格布局很灵活,在需要时可以添加额外的列和行
  • 包括“将尽可能多的列添加到容器”特性

对齐控制(alignment)

  • 可以控制单个网格的对齐
  • 可以控制整体网格的对齐

重叠内容控制(overlap)

  • 一个网格上可以放置多个元素,可以部分重叠,可以通过z-index属性做重叠
  • 当grid与其他的CSS类似flexbox结合时,可以构建出之前无法构建的布局

Grid container、Grid Tracks(fr、repeat()、显隐式grid、minmax)、Grid lines、Grid cells、Grid areas、Gutters

Grid container

  • 通过display:grid或display:inline-grid可以创建出一个grid container
  • 创建了grid container之后,所有grid container下属的子元素都成为了grid item
<div class="wrapper">
  <div>One</div>
  <div>Two</div>
  <div>Three</div>
  <div>Four</div>
  <div>Five</div>
</div>
.wrapper{
    display: grid;
}

通过为.wrapper设置display:grid属性,从默认的block变为grid,构建出一个grid container。
打开devtool选中某个grid item时,grid会有明显的虚线;普通布局不会有。

grid布局:
image

普通布局:
image

Grid Tracks

  • 通过grid-template-columns和grid-template-rows定义行和列,叫做grid track,一个grid track指的是两条线之间的空间
  • 与Grid track相关的概念还有:fr、repeat()函数、隐式grid和显式grid、track大小和minmax
row track和column track

这是一个row track
image

可以通过grid-template-columns定义列track的宽度

<div class="wrapper">
  <div>One</div>
  <div>Two</div>
  <div>Three</div>
  <div>Four</div>
  <div>Five</div>
</div>
.wrapper {
  display: grid;
  grid-template-columns: 200px 200px 200px;
}

image

fr、repeat()、显隐式grid、minmax
fr

track可以通过任何长度单位定义。
grid同样引入了一种特殊的长度单位去创建灵活的grid track。
这个单位就是fr,它代表的是网格容器中可用空间的一个部分。
下面的grid定义会创建三个等宽的track,宽度根据可用空间进行grow和shrink。

<div class="wrapper">
  <div>One</div>
  <div>Two</div>
  <div>Three</div>
  <div>Four</div>
  <div>Five</div>
</div>
.wrapper {
  display: grid;
  grid-template-columns: 1fr 1fr 1fr;
}

image

如果1和4想占据2个单位的宽度的话,可以这样:

.wrapper {
  display: grid;
  grid-template-columns: 2fr 1fr 1fr;
}

下面这样的如何表现呢?

.wrapper {
  display: grid;
  grid-template-columns: 500px 1fr 2fr;
}

第一个track 500px宽;剩余的空间3等分,按照1比2的比例分配出去。
image

repeat()函数

如果有很多重复的track的话,可以用repeat()函数来写。
例如下面的例子:

.wrapper {
    display: grid;
    grid-template-columns: 1fr 1fr 1fr;
}

可以写成这样:

.wrapper {
    display: grid;
    grid-template-columns: repeat(3, 1fr);
}

不仅仅可以单独使用repeat()函数,还可以与px以及其他的组合使用。

.wrapper {
     display: grid;
     grid-template-columns: 20px repeat(6, 1fr) 20px;
}

而且可以repeat多个track,例如下面这样:
5个1:2分割的track,总计10个。

.wrapper {
    display: grid;
    grid-template-columns: repeat(5, 1fr 2fr);
}
显隐式grid
  • 如果只通过grid-template-columns去定义列track的话,grid也会同时创建行,这些行时是隐式grid布局形成的
  • 显式的行grid和列grid,是通过grid-template-columns和grid-template-rows定义出来的
  • 可以通过grid-auto-rows和grid-auto-columns定义隐式grid的tracks

这个例子通过grid-auto-rows,确保隐式的grid高度为200px。

<div class="wrapper">
  <div>One</div>
  <div>Two</div>
  <div>Three</div>
  <div>Four</div>
  <div>Five</div>
</div>
.wrapper {
  display: grid;
  grid-template-columns: repeat(3, 1fr);
  grid-auto-rows: 200px;
}

image

minmax
  • 创建明确grid布局或者自动创建row和column时,我们希望给track一个最小的大小,同时希望任何添加的内容都能展开。例如,假设我想要我的行不比100像素小,但是我的内容最多拉伸到300像素高,就可以通过minmax()函数来实现。
  • minmax()函数仅仅适用于grid布局。
.wrapper {
  display: grid;
  grid-template-columns: repeat(3, 1fr);
  grid-auto-rows: minmax(100px, auto);
}
<div class="wrapper">
  <div>One</div>
  <div>Two
    <p>I have some more content in.</p>
    <p>This makes me taller than 100 pixels.</p>
  </div>
  <div>Three</div>
  <div>Four</div>
  <div>Five</div>
</div>

image

Grid lines
  • grid布局我们定义的是track,但是可以通过grid line去为单个track定义占比
  • grid line从1开始计数
    image

来看一个在这些属性上运用了grid line的例子:

  • grid-column-start
  • grid-column-end
  • grid-row-start
  • grid-row-end
<div class="wrapper">
  <div class="box1">One</div>
  <div class="box2">Two</div>
  <div class="box3">Three</div>
  <div class="box4">Four</div>
  <div class="box5">Five</div>
</div>
.wrapper { 
  display: grid; 
  grid-template-columns: repeat(3, 1fr); 
  grid-auto-rows: 100px; 
} 

.box1 { 
  grid-column-start: 1; 
  grid-column-end: 4; 
  grid-row-start: 1; 
  grid-row-end: 3; 
}

.box2 { 
  grid-column-start: 1; 
  grid-row-start: 3; 
  grid-row-end: 5; 
}

image

Grid cells
  • grid中的最小单位是grid cell
  • 最左上角的cell,grid-column-start/grid-column-end和grid-row-start/grid-row-end都为1
    image
Grid areas
  • grid item可以占据多个grid cell,row方向和column方向都行,占据了多个cell叫做area
  • grid area必须是矩形,不能构建出L形的grid区域
Gutter
  • 可以通过column-gap和row-gap为grid cell设置grid cell之间的间隙
  • 通常为了布局美观,需要为grid container增加一个padding
.wrapper {
  display: grid;
  grid-template-columns: repeat(3, 1fr);
  column-gap: 10px;
  row-gap: 1em;
}
<div class="wrapper">
  <div>One</div>
  <div>Two</div>
  <div>Three</div>
  <div>Four</div>
  <div>Five</div>
</div>

image

嵌套grid布局

<div class="wrapper">
  <div class="box box1">
    <div class="nested">a</div>
    <div class="nested">b</div>
    <div class="nested">c</div>
  </div>
  <div class="box box2">Two</div>
  <div class="box box3">Three</div>
  <div class="box box4">Four</div>
  <div class="box box5">Five</div>
</div>

image

.box1 {
  grid-column-start: 1;
  grid-column-end: 4;
  grid-row-start: 1;
  display: grid;
  grid-template-columns: repeat(3, 1fr);
}

image

  • 通过display: grid;grid-template-columns: repeat(3, 1fr);将第一行内的子元素变为grid布局
  • 通过grid-row-start: 1;控制box1的顺序;grid-row-start: 2;grid-row-start: 3;可以将其放置在下面

grid-row-start: 2
image
grid-row-start: 3
image

grid实现覆盖式的布局并通过z-index控制Grid层级、控制Grid顺序

grid实现覆盖式的布局

grid布局不可做覆盖式的布局。

<div class="wrapper">
  <div class="box box1">One</div>
  <div class="box box2">Two</div>
  <div class="box box3">Three</div>
  <div class="box box4">Four</div>
  <div class="box box5">Five</div>
</div>
.wrapper {
  display: grid;
  grid-template-columns: repeat(3, 1fr);
  grid-auto-rows: 100px;
}

.box1 {
  grid-column-start: 1;
  grid-column-end: 4;
  grid-row-start: 1;
  grid-row-end: 3;
}

.box2 {
  grid-column-start: 1;
  grid-row-start: 2;
  grid-row-end: 4;
}

image

通过z-index控制Grid层级、控制Grid顺序

.wrapper {
  display: grid;
  grid-template-columns: repeat(3, 1fr);
  grid-auto-rows: 100px;
}

.box1 {
  grid-column-start: 1;
  grid-column-end: 4;
  grid-row-start: 1;
  grid-row-end: 3;
  z-index: 2;
}

.box2 {
  grid-column-start: 1;
  grid-row-start: 2;
  grid-row-end: 4;
  z-index: 1;
}

image

学习和实践感悟

  • grid布局与flex布局最大的不同,在于实现多行布局更加便捷,这是因为在一个flex容器内,实现多行布局较为困难,而grid可以很方便的通过grid lines去划分自己占据的空间
  • css tricks上的文章所说的 “grid布局适用于更大型的布局,宏观布局” ,这句话我认为是非常正确的;现代化的布局,应该是 “宏观grid + 微观flex布局” 的组合