flex 布局是现在前端中最流行的布局方式,他能用几个简单的几个属性就能实现我们理想的布局。比如给父元素加上justify-content: center;align-items: center; 就能实现子元素父元素里面上下左右居中。加上 justify-content: space-between; 就能实现两端对齐。 flex 布局给前端开发者带来了很大的便利。但是有时候,使用flex 布局的一些奇怪现象又会给前端开发者带来一些困惑,比如子元素为什么高度自动拉升成了和父元素一样高了啊,父元素的宽高为什么不生效了等奇怪现象。所以本文将全面深入的去探索flex 布局的特性,解决这些困惑。
一、 认识flex 布局
先来看张图:
这张图很清晰描述了flex 布局:
1. flex 布局要有个父容器,比如这里的flex-container
2. flex 布局主要是影响子元素(这里的flex item)的排版方式
3. flex 布局有主轴和交叉轴,默认情况下主轴是横向的,也就是这里的main axis,交叉轴是纵向的,也就是这里的cross axis
4. 默认情况下子元素 会沿着主轴的方向从从左向右进行排列,main start 到,main end
5. 默认情况下子元素会沿着交叉轴默认从上到下排列,当然默认情况下flex 只会排成一行,所以看不出效果。需要添加flex-wrap: wrap等属性之后才能看出效果
我们来看一下flex 的默认情况下的布局:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>felx 布局</title>
<style>
.flex-container {
display: flex;
background: #83c7ff;
width: 500px;
height: 500px;
}
.flex-item {
font-size: 20px;
width: 100px;
height: 100px;
background: #fea490;
}
</style>
</head>
<body>
<div class="flex-container">
<div class="flex-item">
1
</div>
<div class="flex-item">
2
</div>
<div class="flex-item">
3
</div>
</div>
</body>
</html>
以上就是一个默认的flex 布局代码,来看下效果:
可以看到现象和我们上面的图描述的一样。这是默认的,我们可不可以改变这种默认行为呢?肯定是可以的。接下来我们看看改变主轴交叉轴的方案呢。
二、改变主轴交叉轴的方式(flex-direction)
flex-direction 是用来改变主轴的和交叉轴方向的,它是加在父元素上的css属性。它主要有4个常用的值:
- row 默认行为
- row-reverse 将主轴的起点和终点倒过来,也就是main start 和main end 会颠倒过来
- column 将主轴变成纵向的,将交叉轴变成横向的,也就是cross start 和cross end会颠倒过来
- column-reverse 将主轴变成变成横向的,将交叉轴变成横向的同时,将main start,main end, cross start, cross end 进行颠倒过来
看看现象: row
row-reverse
column
column-reverse
三、换行(flex-wrap)
flex 布局默认是一行展示,是不会换行的,哪怕是子元素宽度超出父元素。那么可不可以更改这种行为呢?我可以在父元素上添加flex-wrap属性来更改这种行为。flex-wrap 一共用3个属性值:
- nowrap 默认值, 不换行
- wrap 换行
- wrap-reverse 反向换行(用得比较少)
先将之前的flex-direction 属性删掉,再来演示效果:
nowrap
我们来多添加几个子元素
可以看到子元素的总宽度计算出来,实际已经超过了父元素,子元素还是没有换行。并且发现子元素没有超出父元素,说明子元素的实际宽度被压缩了。我们来审查元素看下是不是这样的
我们可以看到子元素的宽度确实被压缩了,我们写的是100px, 实际渲染出来是83.34px。
wrap
可以看到,超出了的部分就自动换行了,并且你会发现换行的部分距离第一排有一定距例,这是因为flex-wrap:wrap; 的换行的对齐方式是,每一行会平分父容器,平分父容器之后,每行会从平分的顶点开始排列。
wrap-reverse
nowrap 且flex-direction: column;
wrap 且flex-direction: column;
wrap-reverse 且flex-direction: column;
还有flex-direction: row-reverse和flex-wrap的组合,flex-direction: column-reverse的组合,这里就不演示了,可以自己去尝试组合下。
四、flex-flow
flex-flow是flex-direction和flex-wrap组合的简写方式。第一个属性值是flex-direction的值,第二个值是flex-wrap 的值,以空格隔开即可。来演示几个例子:
flex-flow: row nowrap
flex-flow: row-reverse wrap
flex-flow: column-reverse wrap-reverse
五、justify-content 主轴的对齐方式
justify-content 常用的有以下几个值:
- flex-start 以main start 对齐
- flex-end 以main end 对齐
- center 中间对齐
- space-between 两端对齐,剩余的距离平均分配
- space-around子元素两边的距离相等(会造成到父元素两边的距离是子元素到子元素之间的0.5背)
- space-evenly 平均分配剩下的距离
演示效果:
flex-start
flex-end
center
space-between
space-around
space-evenly
六、交叉轴对齐,align-content
align-content 常用的有以下几个值:
- stretch 拉升(默认行为)
- flex-start
- flex-end
- center
- space-around
- space-between
- space-evenly
stretch
我们发现好像并没有拉升,这是为什么呢?因为我们给子元素写了高度,我们将子元素的高度删除。
可以看到子元素的高度就会被拉升到和父元素一样高。
flex-start
flex-end
可以看到flex-end 没有什么效果。是什么原因呢? 因为align-content只有设置了换行才会有效果, 添加flex-wrap: wrap;
添加超过一行的子元素
center
space-around
space-between
space-evenly
七、针对交叉轴每一行的对齐方式 align-items
align-items 主要有以下一个值:
- stretch 拉升(默认值)
- flex-start 从每一行平均分配到容器的开始位置开始排列
- flex-end 从每一行平均分配到容器的结束位置开始排列
- center 每一行平均分配到容器的中间对齐
- baseline 以子元素的第一行文字为基准进行对齐
stretch
和aligin-content一样值为stretch时的效果需要将子元素的高度去掉才能看到效果
flex-start
flex-end
center
baseline 为了看效果,我们先给子元素加一些行高
.flex-item:nth-child(1) {
line-height: 50px;
}
.flex-item:nth-child(2) {
line-height: 80px;
}
.flex-item:nth-child(3) {
line-height: 100px;
}
八、flex-grow 子元素对剩余空间分配
flex-row 是加在子元素上的属性;主要有以下几个值。后面的是数字。不同的数字会有以下几种情况:
1. 默认值为0, 即不占用剩余空间。
2. 子元素的flex-grow数字之和大于等于1的时候会占满一行,且多个子元素的时候,会把剩余的空间按照加起来为百分之百,进行比例分配
3. 数字小于0的时候剩余空间会被分成10份,每个子元素所占用的剩余空间等于所填写的值
一个元素且flex-grow 为1的情况
一个元素且flex-grow小于1的情况
审查元素浏览器还会帮你把真实宽度,和flex-grow 所占用的宽度标出来。带箭头的那部分就是flex-grow之后所占用的剩余空间
多个元素且flex-grow的和大于1的情况:
.flex-item:nth-child(1) {
flex-grow: 1;
}
.flex-item:nth-child(2) {
flex-grow: 2;
}
可以看到第二个子元素占用的剩余空间是第一个子元素的倍,并且子元素填充满了父元素。因为他们的flex-grow之和大于1, 且第二个子元素是第一个子元素的两倍
多个子元素且flex-grow 小于1的情况:
可以看到,第二个子元素占用的剩余宽度同样是第一个的两倍,但是子元素没有沾满父元素,因为它们的和小于1。
九、flex-shrink收缩比
flex-shrink 主要针对子元素宽度之和大于父容器的情况,一般会有以下几种情况:
- 默认值1,收缩到和父容器一样宽
- 子元素flex-shrink 之和大于1,和默认的一样,收缩到和父容器一样
1个子元素的默认的情况
将子元素宽度设置为大于父元素的宽度
.flex-item {
font-size: 20px;
width: 600px;
height: 100px;
background: #fea490;
flex-shrink: 1;
}
一个子元素flex-shrink值为2,可以看到同样被收缩到和父元素一样的宽度。审查元素也可以看到收缩了多少少宽度,箭头的地方就是收缩的宽度。
1个子元素,flex-shrink的值小于1的时候,比如0.5
可以看到收缩了超出的一半,
一个子元素,flex-shrink 值为0的情况:
可以看到完全没有收缩了。
多个子元素,子元素flex-shrink的值都为1的情况:
可以看到多个子元素,且flex-shrink 值相同的情况,收缩的宽度的比例等于子元素的宽度的比例。
多个子元素且flex-shrink 的值不一样的情况
.flex-item:nth-child(1) {
width: 200px;
flex-shrink: 5;
}
.flex-item:nth-child(2) {
width: 400px;
flex-shrink: 1;
}
可以看到,第一个子元素的宽度虽然比第二个的子元素宽要小,但是第一个元素的收缩宽度比第二个元素的收缩宽度要大,这是怎么计算的呢?
第一个子元素搜索宽度= 200 *5 (200 * 5 + 400 *1 ) * 100(子元素之和超出父元素的宽度)
第二个子元素收缩宽度 = 400* 1 (200 * 5 + 400 * 1) * 100(子元素之和超出父元素的宽度)
十、flex-basis 占据主轴空间的大小
flex-basis 主轴方向的影响,主轴是横着的,则flex-basis值会影响宽度,主轴纵向的,则flex-basis的值会影响高度。来看看以下情况
- 主轴横向,flex-basis: auto(默认值)
2. 主轴横向,flex-basis: 200px;
.flex-item {
font-size: 20px;
width: 100px;
height: 100px;
background: #fea490;
flex-basis: 200px;
}
会发现width 被覆盖了,元素渲染宽度变成了200px
- 主轴横向flex-basis: 100%;
- 主轴纵向,flex-basis: 200px;
可以看到主轴纵向时,flex-basis 会覆盖元素的高度
- 主轴纵向,flex-basis: 100%;
十一、flex(flex-grow, flex-shrink,flex-basis 三个值的缩写)
flex 有以下几种情况:
- flex:auto; 是以下三个值的缩写
flex-grow: 1;
flex-shrink: 1;
flex-basis: auto;
- flex: 1;是以下值的缩写
flex-grow: 1;
flex-shrink: 1;
flex-basis: 0%;
3.flex: 0;是以下值的缩写
flex-grow: 0;
flex-shrink: 1;
flex-basis: 0%;
- 写3个参数,flex: 1 0 50%; 可以看到分别代表以下的设置:
十二、order 排序
order 属性值都是数字,默认是0,数字越小的排在越面前,越大的越在后面。来看下面这段代码。
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>felx 布局</title>
<style>
.flex-container {
display: flex;
background: #83c7ff;
width: 500px;
height: 500px;
}
.flex-item {
font-size: 20px;
width: 100px;
height: 100px;
background: #fea490;
}
.flex-item:nth-child(1) {
order: 1
}
.flex-item:nth-child(5) {
order: -1
}
</style>
</head>
<body>
<div class="flex-container">
<div class="flex-item">
1
</div>
<div class="flex-item">
2
</div>
<div class="flex-item">
3
</div>
<div class="flex-item">
4
</div>
<div class="flex-item">
5
</div>
</div>
</body>
</html>
可以看到第一个元素排到了最后,因为它的order值最大,最后一个元素排到第一的位置,因为它的值最小。
十三、align-self
align-self 是针对子项当中的某一个元素的对齐(交叉轴方向),默认值跟随父级的align-items
来看看align-self的用法:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>felx 布局</title>
<style>
.flex-container {
display: flex;
background: #83c7ff;
width: 500px;
height: 500px;
}
.flex-item {
font-size: 20px;
width: 100px;
height: 100px;
background: #fea490;
}
.flex-item:nth-child(1) {
font-size: 20px;
align-self: center;
}
</style>
</head>
<body>
<div class="flex-container">
<div class="flex-item">
1
</div>
<div class="flex-item">
2
</div>
<div class="flex-item">
3
</div>
<div class="flex-item">
4
</div>
<div class="flex-item">
5
</div>
</div>
</body>
</html>
第一项就单独在交叉轴上居中了。
到这里,flex 布局的特性特性就介绍完了。来总结下
十四、总结
在父容器上我们可以通过, flex-direction、flex-wrap、justify-content、align-content、align-items、flex-flow 来改变子元素的排版方式。 在子元素上我们可以通过 flex-grow、flex-shrink、flex-basis、flex、order、align-self 来改变排版方式,利用这些特性进行组合,我们在开发中就能实现各种各样的布局。