上周,我写的 Flex专题 被阮一峰的「每周分享第 48 期」收录,而后 仓库 就登上了子类的 GitHub Trending (流量太可怕了😅)。css
这一次是Flex专题的姊妹篇Grid专题。正巧昨天阮一峰也发布了 Grid 布局教程,你们能够切换着看呐 (我以为个人写得更好,不接受反驳🤔)。html
本文是『horseshoe·Grid专题』系列文章之一,后续会有更多专题推出git
GitHub地址(持续更新):horseshoegithub
博客地址(文章排版真的很漂亮):matiji.cn算法
若是以为对你有帮助,欢迎来 GitHub 点 Star 或者来个人博客亲口告诉我框架
Grid Layout
叫栅格布局模型,由于几乎每个成熟的CSS框架都会实现本身的栅格布局系统,因此W3C干脆弄了一套CSS原生的栅格布局系统,补足这方面的短板。ide
有那么一段时间,网页布局是表格的天下。用表格布局虽然怪怪的,可是表格有它本身的优点。流式布局只能一个一个元素往页面底部流动,它的表达能力是有限的;而表格把页面切成若干豆腐块,能从容调配每一块豆腐,布局表达能力秒杀流
。然而表格毕竟是表格,它有一些特性是专门为制做表格准备的,因此也就逐渐式微了。函数
栅格布局系统,能够理解为更加通用、更增强大的表格布局系统。它也是把页面切成若干豆腐块,元素能够自由声明占用哪一个豆腐块。W3C还为Grid Layout
增长了好多专用的属性、语法和计算函数,能够说,这一次是奔着一劳永逸来的。布局
咱们以前讲过Flexbox属于一维布局模型,详情见:Flex专题。flex
这个专题要讲的Grid Layout
则属于二维布局模型。
当咱们的视角确立之后,所谓的一维就是只有行,而所谓的二维就是有行也有列。很好理解,既然是栅格嘛,那必须得行列相交才能肯定一个格子的大小。
由于Grid Layout
是二维布局模型,它干脆就固定了行与列的方向。格子嘛,任何横着放的布局方式均可以用竖着放的布局方式实现,反之亦然。因此自定义主轴的方向意义不大,因而乎Grid Layout
也就没有主轴和交叉轴的概念。
Grid Layout
惟一涉及到自定义方向的属性是grid-auto-flow
,它的意思是当格子没有显式声明位置的时候,排列顺序的方向如何肯定,是按列排呢仍是按行排。这个属性后面会细讲。
Grid Layout
有栅格容器(grid container),它负责划分领地,容器以内的元素才会臣服于栅格模型;Grid Layout
也有栅格项目(grid item),它们是须要被栅格模型约束的对象。
栅格线能够理解为栅格的边框,水平和垂直的栅格线交叉造成了栅格单元。栅格线有什么做用呢?有些栅格项目可能不止占用一个栅格单元,声明的时候就能够说我从第几条栅格线开始,到第几条栅格线结束,这块区域是老子的
。
你能够为栅格线命名。
四条栅格线合围成的最小区域就是栅格单元。它就是咱们常说的格子。
须要特别区分
栅格单元
与栅格项目
。把栅格模型类比成养猪场的话,栅格单元就是猪圈,栅格项目就是猪。可是这里的猪比较金贵,一个猪圈最多只能养一头猪。就是一个萝卜一个坑吧。
但有些猪比较肥,或者比较霸道,它可能占用不止一个猪圈。
栅格单元是格子,栅格项目是元素,有时候一个元素只须要一个格子约束它,有时候一个元素须要多个格子约束它。
栅格单元的数量是须要显式声明的。若是栅格项目的数量超过了声明的栅格单元的数量,Grid Layout
就会自动建立若干栅格单元来包裹那些超出的栅格项目。
咱们称它为编外栅格单元。
编外栅格单元有本身的特性,能够经过grid-auto-columns
、grid-auto-rows
和grid-auto-flow
自定义。
栅格系统就是栅格单元的总和。
栅格系统和栅格容器不是一个概念,正如栅格单元和栅格项目不是一个概念同样。
栅格系统有可能溢出栅格容器,也可能偏居栅格容器的一隅,也可能充满栅格容器。
仍是回到二维布局模型,虽然咱们说它有行也有列,但区分行与列的收益并不大,因此就统一叫它们栅格轨道。
两条相邻的栅格线与栅格容器合围成的区域就是栅格轨道。
任意四条栅格线合围成的区域均可以成为栅格区域。当一个元素须要多个格子约束它的时候,咱们说这个元素须要一个栅格区域约束它。
栅格区域能够由一个栅格单元组成,也能够有若干个栅格单元组成,但它必须是一个长方体。
或者说,你用两条水平线和两条垂直线组成一个非长方体给我看看?
栅格区域最终是要被栅格项目使用的。你能够给栅格区域命名,栅格项目用名字声明区域,或者栅格项目直接用四条栅格线肯定一个区域。
从这里开始,咱们就要讲具体的CSS属性了。
这个属性声明的是栅格容器的类型。
.container {
display: grid | inline-grid | subgrid;
}
复制代码
前两个属性值的区别在于容器自身应该表现为块元素仍是行内元素。第三个属性值属于CSS Grid Level 2
规范,目前(2019年3月)还在草案阶段,按下不表。
这两个属性声明的是栅格轨道的数量以及宽度。
当你声明了四个宽度值,那在这个方向上就有四条轨道,它们的宽度是你声明的值。
你也能够在声明栅格轨道的同时声明栅格线的名称。顺序就是它们的物理顺序。
.container {
grid-template-columns: <length> <length> | <line-name> <length> <line-name> <length> <line-name>;
grid-template-rows: <length> <length> | <line-name> <length> <line-name> <length> <line-name>;
}
复制代码
任何适用于width
的值都适用于这里。
若是某条栅格轨道的值是auto
,默认状况下该栅格轨道会充满栅格容器的富余空间。
.container {
display: grid;
grid-template-columns: 100px auto 100px;
grid-template-rows: repeat(2, 50px);
grid-gap: 10px;
}
.item.b {
width: 100px;
}
复制代码
可是若是声明了justify-content
(后面会讲到)不为stretch
,那auto
会表现为以栅格项目的长度为准。
.container {
display: grid;
grid-template-columns: 100px auto 100px;
grid-template-rows: repeat(2, 50px);
grid-gap: 10px;
justify-content: start;
}
.item.b {
width: 50px;
}
复制代码
这不难理解。类比一下,普通的块级元素会占满行内的富余空间,绝对定位后的块级元素会以自身或者子元素的宽度为宽度。auto
在这里的表现是同样的。
fr
是fraction
的缩写,翻译成中文是分数
,多少分之一的分数。它是Grid Layout
专用的长度单位。
它的计算公式是这样的,首先减去非fr
单位的长度,以富余空间为总长度,以声明的fr
数量总和为分母,以自身声明的fr
数量为分子,求得自身所占的长度。
.container {
display: grid;
grid-template-columns: 100px repeat(2, 1fr);
grid-template-rows: repeat(2, 50px);
grid-gap: 10px;
}
复制代码
为何W3C要增长一个这样的单位呢?不是有%
么?
答案就在富余空间
上。fr
也是一种百分比,可是它能保证总长度不会超过栅格容器的长度,由于它瓜分的是富余空间的长度;而%
瓜分的是栅格容器的长度,无论别人瓦上霜。
好比说像上面的例子,用%
可得好好算算,用fr
就简单多了。
minmax()
是Grid Layout
专用的计算函数。
.container {
display: grid;
grid-template-columns: minmax(100px, 200px) 300px 300px;
}
复制代码
它有两个参数,分别是最小值和最大值。当栅格单元须要压缩时,最小值就是栅格项目被压缩的最小极限,当栅格单元有剩余空间时,最大值就是栅格项目扩张的最大极限。
它还有几个值须要特别提一下。
fit-content()
也是Grid Layout
专用的计算函数。
它接受一个长度单位的参数。
.container {
display: grid;
grid-template-columns: repeat(3, fit-content(200px));
grid-template-rows: repeat(2, 50px);
grid-gap: 10px;
}
复制代码
咱们已经了解过min-content
和max-content
。
fit-content()
计算公式形象的讲,最小值是内容的min-content
,最大值则取参数和max-content
更小的那个。好比上面的例子,当内容小于200px
时,之内容为长度,当内容大于200px
时,以200px
为长度。
repeat()
是Grid Layout
专用的重复函数。
它接受两个参数,第一个参数是重复的次数,第二个参数是栅格轨道的宽度。
.container {
display: grid;
grid-template-columns: repeat(3, 100px);
grid-template-rows: repeat(2, 50px);
grid-gap: 10px;
}
复制代码
第二个参数不只仅能够是宽度,它能够是一种模式。好比说[col-start] 100px [col-end] auto
,它会重复这一整段若干遍,而中括号包围的是给栅格线命名。
如此这般,第1、3、五条栅格线叫col-start
,第2、4、六条栅格线叫col-end
。总之用repeat函数命名栅格线会有不少重复的名字。
除此以外,第二个参数还能够是minmax
函数、fit-content
函数,或者min-content
、max-content
、auto
关键字。
.container {
display: grid;
grid-template-columns: repeat(3, [col-start] 100px [col-end] auto);
grid-template-rows: repeat(2, 50px);
grid-gap: 10px;
}
复制代码
第一个参数也有两个关键字auto-fill
和auto-fit
。
.container {
display: grid;
grid-template-columns: repeat(auto-fill, 100px);
grid-template-rows: repeat(2, 50px);
grid-gap: 10px;
}
复制代码
.container {
display: grid;
grid-template-columns: repeat(auto-fit, 100px);
grid-template-rows: repeat(2, 50px);
grid-gap: 10px;
}
复制代码
稍微讲解一下。
auto-fill
和auto-fit
的共同点在于它们会保证栅格系统不溢出栅格容器。由于若是你写死了重复多少个,栅格容器空间不够的话只能溢出了。
而不一样点在于,auto-fill
会生成尽量多的栅格轨道,即使这些轨道看起来没什么用;auto-fit
则会生成尽量少的栅格轨道,以便让那些自适应的栅格单元尽量占用更多空间。
因此区别在于,auto-fill
想让栅格轨道尽量多,auto-fit
想让栅格单元尽量大。
这两个属性是Grid Layout
自适应布局的利器,连媒体查询都省了。
.container {
display: grid;
grid-template-columns: repeat(auto-fit, minmax(300px, 1fr));
}
复制代码
这里的意思是说,自适应布局,每一个栅格项目长度等分,但最小不低于300px
。那最大怎么肯定呢?600px
到900px
之间,一行只能放两个项目,一旦超过900px
,一行就会放三个项目。
这个属性给栅格单元命名,同名的栅格单元自动成为一个栅格区域。
.container {
display: grid;
grid-template-columns: repeat(3, 1fr);
grid-template-areas:
'header header header'
'sidebar main main'
'footer footer footer';
}
复制代码
命名以后有什么用呢?栅格项目有一个grid-area
属性,它来瓜分栅格区域。
.item {
grid-area: header;
}
复制代码
这个意思是说,名字叫header
的栅格区域都是个人,撒尿为证。
用grid-template-columns
和grid-template-rows
声明了多少个栅格单元,命名的时候须要名字与栅格单元一一对应起来。而且前面说过栅格区域必须是长方体,连续命名的时候也要注意这点。
我偏不一一对应呢?好比横向上有三个栅格单元,但我只声明两个名字。
.container {
display: grid;
grid-template-columns: repeat(3, 1fr);
grid-template-areas:
'header header';
}
复制代码
或者我偏不凑一个长方体呢?好比第一行两个main
,第二行三个main
。
.container {
display: grid;
grid-template-columns: repeat(3, 1fr);
grid-template-rows: repeat(2, 50px);
grid-template-areas:
'main main avatar'
'main main main';
}
.item.a {
grid-area: main;
}
复制代码
格式不对的话,结果都是同样的。全部声明了grid-area
的项目都会在右下角的某个编外栅格单元内,重叠在一块儿,尚不清楚它的算法或机制是怎样的。
其实第二种状况,彻底能够认为两列四个main
组成一个区域,另外一个main
组成另外一个区域,是吧?可是你想一想,如今有两个叫main
的区域,项目瓜分的时候很尴尬呀,有一块飞地。格式仍是不对。
因此呀,语法就是这么严格,老老实实遵照。
既然语法这么严格,你要知道,取名字是一件脑袋疼的事情啊。我明明只须要给一小块区域取名字,你非得让我取满。有没有什么省事的办法呢?
固然有。不知道叫什么的时候就用.
代替。
.container {
display: grid;
grid-template-columns: repeat(3, 1fr);
grid-template-areas:
'main main sidebar'
'main main .';
}
复制代码
不只如此,只要没有空格分开,n个.
都只占一个栅格单元。
grid-template-areas
还有一个属性值none
。一开始我觉得打开方式是这样的:
.container {
display: grid;
grid-template-columns: repeat(3, 1fr);
grid-template-areas:
'main main sidebar'
'main main none';
}
复制代码
结果我其实声明了一个叫作none
的栅格区域。none
是grid-template-areas
的默认值,实际打开方式是这样的:
.container {
grid-template-areas: none;
}
复制代码
也就是说,别管它。
每个栅格区域都由四条栅格线包裹,这四条线同时会被隐式的赋予名称。横向上分别是xxx-start
和xxx-end
,纵向上也是xxx-start
和xxx-end
。反正栅格线名字不怕多,它只怕黑,由于它是黑怕歌手。
同时呢,反过来也是成立的。
.container {
display: grid;
grid-template-columns: [biu-start] 1fr [nothing] 1fr [biu-end] 1fr [nothing];
grid-template-rows: [biu-start] 1fr [nothing] 1fr [biu-end];
}
复制代码
上面会隐式的声明一个叫作biu
的栅格区域。并且发现了没有,栅格区域不能够重名,可是能够重叠。
这个属性声明的是栅格单元之间的空隙。
.container {
grid-column-gap: <length>;
grid-row-gap: <length>;
}
复制代码
这里的值能够是任何定义width
的值。
能够看到,若是将grid-column-gap
设为80%
,它的意思就是gap
占栅格容器的80%
,全部栅格单元只能分剩下的20%
。
若是将grid-column-gap
设为100%
或超过100%
呢?栅格单元的宽度不必定是0,由于在Flex专题中咱们讲过,margin
、border
和padding
是很刚的,只要你定义了,flex或者grid彻底没法压缩它们。
特别须要注意的是,
grid-[column|row]-gap
没法使用fr
单位的值。配角戏份就不要太多了吧。
这是一个集合属性,能够同时声明grid-column-gap
和grid-row-gap
。
.container {
grid-gap: <grid-column-gap> <grid-row-gap>;
}
复制代码
这个属性声明的是栅格单元相对于垂直栅格线的对齐方式。
.container {
justify-items: stretch(default) | start | end | center;
}
复制代码
.container {
display: grid;
grid-template-columns: repeat(3, 1fr);
grid-template-rows: repeat(2, 50px);
grid-gap: 10px;
justify-items: center;
}
复制代码
当栅格单元的总宽度大于栅格容器的宽度时,垂直栅格线会被撑开,也就不存在对齐了。
因此这个属性只有在栅格单元的总宽度小于栅格容器的宽度时才生效。
Flexbox的相似属性值有
flex-start
和flex-end
,W3C终于在grid上把前缀去掉了。
这个属性声明的是栅格单元相对于水平栅格线的对齐方式。
.container {
align-items: stretch(default) | start | end | center;
}
复制代码
这是一个集合属性,能够同时声明align-items
和justify-items
。
若是省略第二个参数,则第二个参数会采用第一个值。
.container {
place-items: <align-items> <justify-items>;
}
复制代码
这个属性声明的是栅格系统相对于栅格容器的水平对齐方式。
.container {
justify-content: start(default) | end | center | stretch | space-around | space-between | space-evenly;
}
复制代码
.container {
display: grid;
grid-template-columns: repeat(3, 100px);
grid-template-rows: repeat(2, 50px);
grid-gap: 10px;
justify-content: center;
}
复制代码
若是栅格单元声明的宽度都是非auto
的值,那justify-content
的默认值是start
,也就是左对齐。这时候stretch
是不起做用的。
若是有栅格单元的宽度值是auto
,那它默认就是stretch
,因而整个栅格系统也变成stretch
。固然你能够将justify-content
设置成别的值,这时候宽度值是auto
的栅格单元就以子元素的宽度为宽度了。
因此我没明白justify-content: stretch
的做用是什么。惟一的使用场景是样式覆盖的时候。
这个属性声明的是栅格系统相对于栅格容器的垂直对齐方式。
.container {
align-content: start(default) | end | center | stretch | space-around | space-between | space-evenly;
}
复制代码
这里关于stretch
的处理是同样的。
这是一个集合属性,能够同时声明align-content
和justify-content
。
若是省略第二个参数,则第二个参数会采用第一个值。
.container {
place-content: <align-content> <justify-content>;
}
复制代码
这两个属性声明的是编外栅格单元的高度和宽度。
.container {
grid-auto-columns: <length> <length> <length>;
grid-auto-rows: <length> <length> <length>;
}
复制代码
.container {
display: grid;
grid-template-columns: repeat(3, 1fr);
grid-template-rows: repeat(2, 50px);
grid-gap: 10px;
grid-auto-rows: 100px;
height: 300px;
}
复制代码
编外栅格单元的宽度默认值都是auto。也就是说,以宽度举例,若是栅格容器的宽度大于栅格系统的宽度,那编外栅格单元会平分富余空间的宽度,不然编外栅格单元就之内容的宽度为宽度了。
.container {
display: grid;
grid-template-columns: repeat(3, 1fr);
grid-template-rows: repeat(2, 50px);
grid-gap: 10px;
height: 300px;
}
复制代码
.container {
display: grid;
grid-template-columns: repeat(3, 1fr);
grid-template-rows: repeat(2, 50px);
grid-gap: 10px;
height: 110px;
}
复制代码
这个属性声明的是若是栅格项目没有明确指定在栅格容器中的位置时,栅格项目应该如何依次排列。
关于什么叫明确指定位置,栅格项目自身有一些属性,能够声明它占据的区域从哪条栅格线开始,到哪条栅格线结束,或者直接声明占据哪一个栅格区域。这里按下不表,后面会讲到。
这个属性有点像Flexbox的flex-direction
属性。
.container {
grid-auto-flow: row(default) | column | dense | row dense | column dense;
}
复制代码
顾名思义,row
就是按行排列,column
就是按列排列。
.container {
display: grid;
grid-template-columns: repeat(3, 100px);
grid-template-rows: repeat(2, 50px);
grid-gap: 10px;
grid-auto-flow: row;
}
复制代码
.container {
display: grid;
grid-template-columns: repeat(3, 100px);
grid-template-rows: repeat(2, 50px);
grid-gap: 10px;
grid-auto-flow: column;
}
复制代码
重点要谈谈的是dense
这个属性值。dense
翻译成中文是稠密
的意思,它的做用是当排在前面的栅格项目因为某些缘由(主要是明确指定了位置,可是又没有占满)空出了一些位置,后面的项目若是合身的话应不该该挤进去。
这么干的后果就是没有明确指定位置的栅格项目可能不按顺序排列。
.container {
display: grid;
grid-template-columns: repeat(3, 100px);
grid-template-rows: repeat(2, 50px);
grid-gap: 10px;
}
.item.c {
grid-column-start: 2;
}
复制代码
.container {
display: grid;
grid-template-columns: repeat(3, 100px);
grid-template-rows: repeat(2, 50px);
grid-gap: 10px;
grid-auto-flow: dense;
}
.item.c {
grid-column-start: 2;
}
复制代码
这是一个集合属性,它能够声明两大类属性中任一类的全部属性值。
.container {
grid: <grid-template-rows> / <grid-template-columns>;
grid: <grid-auto-flow> [<grid-auto-rows> [/ <grid-auto-columns>]];
}
复制代码
以长度开头的值声明的就是第一类,以row
、column
或dense
开头的值声明的则是第二类。
以当前Grid Layout
的普及程度来看,尽可能不要这么写,你写的费劲,别人看的费劲。
从这里开始,涉及到的属性都是栅格项目自身的属性。
这个属性声明的是指定栅格项目从哪里开始到哪里结束。
.item {
grid-column-start: auto(default) | <number> | <name> | span <number> | span <name>;
grid-column-end: auto(default) | <number> | <name> | span <number> | span <name>;
grid-row-start: auto(default) | <number> | <name> | span <number> | span <name>;
grid-row-end: auto(default) | <number> | <name> | span <number> | span <name>;
}
复制代码
start
并不必定要比end
靠前,靠后的话,开始到结束的方向就相反了。好比下面两段代码指定的区域是同样的。
.item {
grid-column-start: 1;
grid-column-end: 3;
}
复制代码
.item {
grid-column-start: 3;
grid-column-end: 1;
}
复制代码
咱们来挨个讲解各属性值:
auto
指的是只占用一个栅格单元。不管从哪开始,从哪结束,只要有一个auto
值,它就只占一个栅格单元。
number
指的是栅格线的顺序,从1开始。
name
指的是栅格线的名称。栅格线的名称能够从两个地方来,第一是经过grid-template-[columns|rows]
显示声明,第二是定义grid-template-areas
的同时会为合围的栅格线自动生成xxx-start
和xxx-end
的名称。
span <number>
指的是跨度为几。这里的数字再也不是第几条栅格线,而是跨越几条几条栅格线。
span <name>
指的是跨越到该名称的栅格线为止。它和仅仅是name
有什么区别呢?若是start
比end
靠后,仅仅是name
的状况会像前面说的同样,开始到结束的方向就相反;而span <name>
的状况则会一直日后找,毕竟找不到嘛,因此就跨越到最后一条栅格线。其中的区别在于愣头青的程度对不对?
.container {
display: grid;
grid-template-columns: repeat(3, 100px);
grid-template-rows: repeat(2, 50px);
grid-template-areas:
'a b c'
'd e f';
grid-gap: 10px;
}
.item.c {
grid-column-start: c-start;
grid-column-end: a-start;
}
复制代码
.container {
display: grid;
grid-template-columns: repeat(3, 100px);
grid-template-rows: repeat(2, 50px);
grid-template-areas:
'a b c'
'd e f';
grid-gap: 10px;
}
.item.c {
grid-column-start: c-start;
grid-column-end: span a-start;
}
复制代码
这是两个集合属性,它们能够同时声明在某个方向开始和结束的位置。
.item {
grid-column: <start> / <end>;
grid-row: <start> / <end>;
}
复制代码
这个属性声明的是栅格项目占据哪一个栅格区域。
.item{
grid-area: <name> | <row-start> / <column-start> / <row-end> / <column-end>;
}
复制代码
前面介绍过grid-template-areas
属性,它声明的栅格区域就能够被栅格项目使用了。
固然你也可使用栅格线的方式来合围一个栅格区域,因此它也至关于grid-[column|row]-[start|end]
的终极集合属性。要特别注意声明的顺序。
.container {
display: grid;
grid-template-columns: repeat(3, 100px);
grid-template-rows: repeat(2, 50px);
grid-template-areas:
'a b c'
'd e f';
grid-gap: 10px;
}
.item.c {
grid-area: a; /* 或者 grid-area: 1 / 1 / 2 / 2; */
}
复制代码
这个属性声明的是栅格项目的长度若是小于栅格单元的长度,栅格项目如何水平对齐。
经过它能够声明该栅格项目自身的水平对齐方式,甚至能够覆盖栅格容器justify-items
的值。
.item {
justify-self: stretch(default) | start | end | center;
}
复制代码
这个属性声明的是栅格项目的高度若是小于栅格单元的高度,栅格项目如何垂直对齐。
经过它能够声明该栅格项目自身的垂直对齐方式,甚至能够覆盖栅格容器align-items
的值。
.item {
align-self: stretch(default) | start | end | center;
}
复制代码
这是一个集合属性,能够同时声明align-self
和justify-self
。
若是省略第二个参数,则第二个参数会采用第一个值。
.item {
place-self: <align-self> <justify-self>;
}
复制代码
有一个小游戏 Grid Garden 能够帮助你轻松的实践Grid Layout
的各项特性。
还有一个网站 GridByExample,号称全部你须要知道的Grid Layout
知识都在这里。
本文是『horseshoe·Grid专题』系列文章之一,后续会有更多专题推出
GitHub地址(持续更新):horseshoe
博客地址(文章排版真的很漂亮):matiji.cn
若是以为对你有帮助,欢迎来 GitHub 点 Star 或者来个人博客亲口告诉我