CSS的Flex与Grid布局
Abstract
本文对 CSS 中的 flex 和 grid 两种布局的语法基础与特性做介绍,重点是剖析其中的设定逻辑,以便实际的使用。
Flex 布局
Flex 是 Flexble box 的缩写,意味着使用盒子模型进行可变的布局调整
语法
语法一览
- 对于任意一个块级且内部可以嵌套其他元素的元素,都可以修改其dispaly 属性为 Flex 来使用 flex 布局
- 对于 inline 元素,也可以使用 dispaly: inline-flex 来使用 flex 布局
- flex-diraction:指定内部元素的排列顺序
- flex-warp:是否换行。
- flex-flow:子元素排列方式和换行方式的简写
- justify-content:水平对齐方式
- align-items:垂直对齐方式
- align-contents:多元素块状对齐方式
- 以下是子项目属性
- order:手动指定排列顺序
- flex-grow:放大方式
- flex-shrink:缩小方式
- flex-basis:子元素原始大小
- flex:上三种属性的缩写
- align-self:忽略容器 align-items,单独指定对齐方式
布局行为
首先注意的事情是,Flex 本质是一种布局方式,也就是说一个 flex 布局存在两种身份:容器(container)和项目(items)。
向一个 flex 容器指定 flex 选项,只会影响 flex 容器内部的情况,而不影响本身,换句话说,flex 属性控制的是容器内部的排布情况。向一个项目指定 flex 属性,影响的是这个元素在布局中的行为。
轴向系统
一个 flex 容器的内部有两个轴,一条主轴一条副轴。使用 flex-direction 来定义主轴方向,上下左右。具体的排列行为:所有元素沿着主轴方向排列,在主轴方向排列满之后,向副轴方向换行继续排列。
flex-direction 有四种取值,定义主轴方向:
row——从左到右
row-reverse——从右向左
colum——从上到下
columreverse——从下到上
或许你会好奇,那么副轴的方向怎么确定?
这里需要说明:对于 row 类的排列,副轴始终由上到下,对于 column ,副轴始终从左到右。
那么我如何修改换行方向呢?使用 flex-warp 属性
flex-warp 用来控制换行行为,默认是 nowarp 即不换行,所有的元素按照 flex-direction 指定的方向排布。具体的取值如下
nowarp:不换行,使用 flex-direction 指定方向排列,可以溢出容器边框
warp:默认行为换行:即按照副轴方向,将主轴溢出的内容折到下一行
warp-reverse:如字面,向着反方向换行
上述的两种属性可以直接使用 flex-flow 来简写,格式为
.flexcontainer{
flex-flow:<flex-direction> || <flex-warp>
}
对齐方式
上述两个属性定义元素的排列方向,但是元素在主轴方向上不一定是连续放置的,而是有一些对齐方式,“排列方向”本质上是一种元素分配和顺序的定义,也就是“第一根主轴上有这几个元素,第二根是另一些”,而主轴上的位置由对齐方式决定。
对齐方式使用 justify-content 属性定义
justify-content 有以下几个取值
flex-start:所有元素向主轴起始点对齐
flex-end:所有元素向着主轴方向对齐
center:居中
space-between:两端的元素靠边,中间的空白所有元素均分
space-around:所有项目在主轴方向有相同的类似 margin 的距离值,换句话说,元素之间的间隙是边缘与元素之间距离的两倍。
space-evenly:项目之间,项目与容器边缘之间的所有空隙全部相等
值得说的是,这个属性只有在主轴方向上有空间的时候才有用,如果溢出则没有作用。
以及,margin: auto 的优先级更高,会优先吸纳空间。
在主轴上的对齐方式(水平对齐方式)得到了,副轴方向上的对齐方式(垂直对齐)呢?使用 align-items 来做到
注意这里不是若干行之间的关系,而是主轴一行内的相对对齐。
align-items 有以下几个取值
flex-start,flex-end:同上
center:居中
strech:拉伸到填满本行空间
baseline:对齐所有元素的第一行文字的 underline
考虑完了每一行上的行为,多行之间的对齐方式如何呢?使用 align-content 进行控制。
allign-content 有如下取值
flex-start,flex-end,center
strech:拉伸,均分容器高度
space-between,space-around,space-evenly
含义都同上
子项目属性
order 属性:相当于对元素顺序进行重排,可以取负数,默认为 0。
这里强调一点:order 相同时,按照原始 html 元素顺序来安排
flex-grow,flex-shrink 属性:这两个属性具有相似的含义与同样的计算方式:
对于一行元素,当窗口大小变化,元素尺寸随之变化的行为,grow 控制方式,shrink 控制缩小。具体的算法如下:
定义第 n 个元素的 grow/shrink 值为 $a_\text{n}$,对于窗口边框大小变化值 $\Delta w$, 每一个元素的主轴方向尺寸变化为:
$$
\Delta w_\text{i}=\Delta w* \frac{a_\text{i}}{\sum a_\text{n}}
$$
不难看出,这个行为相当于是给变化值加权,grow 与 shrink 分别控制 $\Delta w$ 是正和负的时候的情况。特别的有以下几个特殊取值:取值 0,代表不变化,剩下的值大小是相对关系。默认为 0。
flex-basis:初始的值,默认为 auto,即原本元素的宽度,可以显式的指定初始大小,和 height/width 类似。
flex 属性:上述三个属性的简便写法,具体的顺序是:
.{
flex: flex-grow flex-shrink flex-basis
}
这个属性有两个默认值:auto(1 1 auto),none(0 0 auto)
align-self 属性:单独控制副轴对齐,覆盖容器的 align-item 选项,取值和 align-item 相同
布局思想
从上述的内容不难考虑到,Flex 本质上是线性的,将若干元素沿着一条线排列,放在容器中若超出容器宽度,则“折段”这一条线,另起一行。这样,flex 实现了二维布局的分配——若干条线。这就是 flex 布局的本质
flex 首先定义这条线的方向(direction),再定义换行的行为(warp)。在定义在同一条线上的元素如何排列(justify,align-items),然后就得到了若干条安排好的线,再排列这几条线(align-content),最后调整一些元素的动态行为(flex),就得到了最终成果
Felx 布局只是安排了容器内部元素的位置,与子元素内部的内容,容器本身和容器外的内容没有任何影响。
Grid 布局
语法
- 同理,display 有 inline-display 两种选项,分别表示块状元素和行内元素。
- grid-tetmplate-columns/rows:指定网格尺寸
- 简写,函数和预载值
- gap 属性
- areas 定义网格
- auto-flow
- justify-items,align-items,palce-irems
- justify-contents,align-contents,contents
- auto-columns/rows
- gird-column/rows-start/end
- justify/align/place-self
布局行为
gird 布局首先需要定义网格属性,有多种方法
方法 1:用到 gird-template-row/column 两个属性按顺序定义每一行/列的宽度,例如
.girdnet{
display:gird
grid-template-row:50px 50px
grid-template-column:100px 50px 250px
}
gird 为若干相同等宽行列提供了节约语法:repeat(),用法为 repeat(times, size)
对于动态版面,repeat 的 times 还有一个特别的值:auto-fill,代表网格自适应列数/行数,只要定好 size 就好。
grid 还提供了一个动态单位:fr,fr 控制容器里没有被显性确定的宽度,使用 fr 的各行按照加权方式(与 flex-grow 相同) 分配格子宽度。
对于网格的宽度范围,grid 还有专门的限制语法 minmax,用法是 minmax(min, max)
当然 size 也有 auto 选项,会默认吞掉所有的空白
gap 属性:网格单元之间的间隔,可以分别指定 column/row 或者两边指定,具体的属性名是 column/row-gap 和 gap
grid 的内容分配:大部分是由子元素决定的,在容器上有一种便利写法:grid-template-areas
areas 属性的值每一行使用一个双引号括起来的字符串表示,格子之间使用空格分割,为每一个单元格取名,相同的名字被视为连在一起。
每一个元素占据哪一个格子,写在子项目里。
例如
.gridnet{
display: grid;
gap: 10px;
grid-template-columns: 120px 120px 120px;
grid-template-areas: ". header header"
"sidebar content content";
/* 注意“.”是默认占位符,表示这个位置不放东西 */
}
内容自动填充:grid 提供了固定的填充方式,不用全部指定,这个属性叫作 grid-auto-flow
grid-auto-flow 有以下几个选项
row:先行后列
column:先列后行
注意:固定是先左后右,先上后下的填充法
单元格对齐方式:
justify/align/place-items 用来控制元素在格子里的相对位置
justify/align/place-items 有以下四个选项:
start:左对齐
end:右对齐
center:居中
strech:拉伸
这里的 palce 是按照 column row 的顺序的简便写法。
布局对齐方式:
justify/align/place-contents 用来控制整个布局小于容器的时候的位置
justify/align/place-contents 有以下几种取值
start 容器开始
end 容器结束
center 居中
space-between/around/evenly:与 flex 相同
strech:拉伸
实际上,还有可能更多没有被指定位置的单元,有为了解决这个问题的两个属性:
gird-auto-columns/rows 属性
有以下几个值:auto(根据容器界定)/max-content(与最宽相同)/min-content(与最窄的相同)/length(自定宽)
子项目属性
首先是 grid 项目有对行列的编号,从左到右从上到下进行 1 base 的编号,然后使用四点定位规定元素占位
gird-column/row-start/end 四个属性,方向从左到右,从上到下,元素间可以重叠,使用 z-index 处理重叠关系。
有一个 span 元素表示跨越若干格子,从默认起始位置开始
gird-area:与容器的 grid-tempaltes-areas 对应,值就是要占据的区域名。
justify/align/place-self:单独指定元素的对齐方式,与-items 有相同的取值
布局思路
可以看出,grid 是布局先于内容的,首先控制布局行为,然后再填充内容。
相比 Flex,flex 是以内容为先的,由内容变化来调整布局,在一维的情况下表现更好,写法也更加简单。