彻底搞懂flex 布局

575 阅读11分钟

flex 布局是现在前端中最流行的布局方式,他能用几个简单的几个属性就能实现我们理想的布局。比如给父元素加上justify-content: center;align-items: center; 就能实现子元素父元素里面上下左右居中。加上 justify-content: space-between; 就能实现两端对齐。 flex 布局给前端开发者带来了很大的便利。但是有时候,使用flex 布局的一些奇怪现象又会给前端开发者带来一些困惑,比如子元素为什么高度自动拉升成了和父元素一样高了啊,父元素的宽高为什么不生效了等奇怪现象。所以本文将全面深入的去探索flex 布局的特性,解决这些困惑。

一、 认识flex 布局

先来看张图:

image.png 这张图很清晰描述了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 布局代码,来看下效果:

image.png

可以看到现象和我们上面的图描述的一样。这是默认的,我们可不可以改变这种默认行为呢?肯定是可以的。接下来我们看看改变主轴交叉轴的方案呢。

二、改变主轴交叉轴的方式(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

image.png

row-reverse

image.png

column

image.png

column-reverse

image.png

三、换行(flex-wrap)

flex 布局默认是一行展示,是不会换行的,哪怕是子元素宽度超出父元素。那么可不可以更改这种行为呢?我可以在父元素上添加flex-wrap属性来更改这种行为。flex-wrap 一共用3个属性值:

  • nowrap 默认值, 不换行
  • wrap 换行
  • wrap-reverse 反向换行(用得比较少)

先将之前的flex-direction 属性删掉,再来演示效果:

nowrap

image.png

我们来多添加几个子元素

image.png 可以看到子元素的总宽度计算出来,实际已经超过了父元素,子元素还是没有换行。并且发现子元素没有超出父元素,说明子元素的实际宽度被压缩了。我们来审查元素看下是不是这样的

image.png 我们可以看到子元素的宽度确实被压缩了,我们写的是100px, 实际渲染出来是83.34px。

wrap

image.png

可以看到,超出了的部分就自动换行了,并且你会发现换行的部分距离第一排有一定距例,这是因为flex-wrap:wrap; 的换行的对齐方式是,每一行会平分父容器,平分父容器之后,每行会从平分的顶点开始排列。

wrap-reverse

image.png

nowrap 且flex-direction: column;

image.png

wrap 且flex-direction: column;

image.png

wrap-reverse 且flex-direction: column;

image.png

还有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

image.png

flex-flow: row-reverse wrap

image.png

flex-flow: column-reverse wrap-reverse

image.png

五、justify-content 主轴的对齐方式

justify-content 常用的有以下几个值:

  • flex-start 以main start 对齐
  • flex-end 以main end 对齐
  • center 中间对齐
  • space-between 两端对齐,剩余的距离平均分配
  • space-around子元素两边的距离相等(会造成到父元素两边的距离是子元素到子元素之间的0.5背)
  • space-evenly 平均分配剩下的距离

演示效果:

flex-start

image.png

flex-end

image.png

center

image.png

space-between

image.png

space-around

image.png

space-evenly

image.png

六、交叉轴对齐,align-content

align-content 常用的有以下几个值:

  • stretch 拉升(默认行为)
  • flex-start
  • flex-end
  • center
  • space-around
  • space-between
  • space-evenly

stretch

image.png 我们发现好像并没有拉升,这是为什么呢?因为我们给子元素写了高度,我们将子元素的高度删除。

image.png

可以看到子元素的高度就会被拉升到和父元素一样高。

flex-start

image.png

flex-end

image.png 可以看到flex-end 没有什么效果。是什么原因呢? 因为align-content只有设置了换行才会有效果, 添加flex-wrap: wrap;

image.png

添加超过一行的子元素

image.png

center

image.png

space-around

image.png

space-between

image.png

space-evenly

image.png

七、针对交叉轴每一行的对齐方式 align-items

align-items 主要有以下一个值:

  • stretch 拉升(默认值)
  • flex-start 从每一行平均分配到容器的开始位置开始排列
  • flex-end 从每一行平均分配到容器的结束位置开始排列
  • center 每一行平均分配到容器的中间对齐
  • baseline 以子元素的第一行文字为基准进行对齐

stretch

和aligin-content一样值为stretch时的效果需要将子元素的高度去掉才能看到效果

image.png

flex-start

image.png

flex-end

image.png center

image.png

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;
}

image.png

八、flex-grow 子元素对剩余空间分配

flex-row 是加在子元素上的属性;主要有以下几个值。后面的是数字。不同的数字会有以下几种情况:

1. 默认值为0, 即不占用剩余空间。

2. 子元素的flex-grow数字之和大于等于1的时候会占满一行,且多个子元素的时候,会把剩余的空间按照加起来为百分之百,进行比例分配

3. 数字小于0的时候剩余空间会被分成10份,每个子元素所占用的剩余空间等于所填写的值

一个元素且flex-grow 为1的情况

image.png

一个元素且flex-grow小于1的情况

image.png 审查元素浏览器还会帮你把真实宽度,和flex-grow 所占用的宽度标出来。带箭头的那部分就是flex-grow之后所占用的剩余空间

多个元素且flex-grow的和大于1的情况:

.flex-item:nth-child(1) {
  flex-grow: 1;
}
.flex-item:nth-child(2) {
  flex-grow: 2;
}

image.png

image.png

可以看到第二个子元素占用的剩余空间是第一个子元素的倍,并且子元素填充满了父元素。因为他们的flex-grow之和大于1, 且第二个子元素是第一个子元素的两倍

多个子元素且flex-grow 小于1的情况:

image.png

可以看到,第二个子元素占用的剩余宽度同样是第一个的两倍,但是子元素没有沾满父元素,因为它们的和小于1。

九、flex-shrink收缩比

flex-shrink 主要针对子元素宽度之和大于父容器的情况,一般会有以下几种情况:

  • 默认值1,收缩到和父容器一样宽
  • 子元素flex-shrink 之和大于1,和默认的一样,收缩到和父容器一样

1个子元素的默认的情况

将子元素宽度设置为大于父元素的宽度

.flex-item {
  font-size: 20px;
  width: 600px;
  height: 100px;
  background: #fea490;
  flex-shrink: 1;
}

image.png

一个子元素flex-shrink值为2,可以看到同样被收缩到和父元素一样的宽度。审查元素也可以看到收缩了多少少宽度,箭头的地方就是收缩的宽度。

image.png 1个子元素,flex-shrink的值小于1的时候,比如0.5

image.png 可以看到收缩了超出的一半,

一个子元素,flex-shrink 值为0的情况:

image.png 可以看到完全没有收缩了。

多个子元素,子元素flex-shrink的值都为1的情况:

image.png

image.png

可以看到多个子元素,且flex-shrink 值相同的情况,收缩的宽度的比例等于子元素的宽度的比例。

多个子元素且flex-shrink 的值不一样的情况

.flex-item:nth-child(1) {
  width: 200px;
  flex-shrink: 5;
}
.flex-item:nth-child(2) {
  width: 400px;
  flex-shrink: 1;
}

image.png

image.png

可以看到,第一个子元素的宽度虽然比第二个的子元素宽要小,但是第一个元素的收缩宽度比第二个元素的收缩宽度要大,这是怎么计算的呢?

第一个子元素搜索宽度= 200 *5 (200 * 5 + 400 *1 ) * 100(子元素之和超出父元素的宽度)

第二个子元素收缩宽度 = 400* 1 (200 * 5 + 400 * 1) * 100(子元素之和超出父元素的宽度)

十、flex-basis 占据主轴空间的大小

flex-basis 主轴方向的影响,主轴是横着的,则flex-basis值会影响宽度,主轴纵向的,则flex-basis的值会影响高度。来看看以下情况

  1. 主轴横向,flex-basis: auto(默认值)

image.png 2. 主轴横向,flex-basis: 200px;

.flex-item {
  font-size: 20px;
  width: 100px;
  height: 100px;
  background: #fea490;
  flex-basis: 200px;
}

image.png

会发现width 被覆盖了,元素渲染宽度变成了200px

  1. 主轴横向flex-basis: 100%;

image.png

  1. 主轴纵向,flex-basis: 200px;

image.png 可以看到主轴纵向时,flex-basis 会覆盖元素的高度

  1. 主轴纵向,flex-basis: 100%;

image.png

十一、flex(flex-grow, flex-shrink,flex-basis 三个值的缩写)

flex 有以下几种情况:

  1. flex:auto; 是以下三个值的缩写
flex-grow: 1;
flex-shrink: 1;
flex-basis: auto;

image.png

  1. flex: 1;是以下值的缩写
flex-grow: 1;
flex-shrink: 1;
flex-basis: 0%;

image.png

3.flex: 0;是以下值的缩写

flex-grow: 0;
flex-shrink: 1;
flex-basis: 0%;
  1. 写3个参数,flex: 1 0 50%; 可以看到分别代表以下的设置:

image.png

十二、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>

image.png

可以看到第一个元素排到了最后,因为它的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>

image.png

第一项就单独在交叉轴上居中了。

到这里,flex 布局的特性特性就介绍完了。来总结下

十四、总结

在父容器上我们可以通过, flex-direction、flex-wrap、justify-content、align-content、align-items、flex-flow 来改变子元素的排版方式。 在子元素上我们可以通过 flex-grow、flex-shrink、flex-basis、flex、order、align-self 来改变排版方式,利用这些特性进行组合,我们在开发中就能实现各种各样的布局。