css 布局三:flex 布局-实操篇

137 阅读10分钟

俗话说:光说不练,等于白干.又曰知行合一,日进千里.

前面已经将flex布局的所有概念都做了详细的总结,但是总感觉距离掌握这玩意儿, 还差点意思,我冥思苦想,总算明白了,还得是实践操作几波,才能用好 flex 布局这把尖刀.

想到哪干到哪,下面基于概念篇 做详细的实操,本篇默认都已 熟悉flex布局的各种概念,看不下去的观众老爷,请移步概念篇.

flex 布局的容器探索

概念篇 我们有说到 要成为 flex 布局的容器,只需要给一个html 元素 添加上 dispaly:flex,或者dispaly:inline-flex,就可以了.这两种方式决定了 容器 容器本身的排布方式.

<span>span1</span>
<div class="container">container</div>
<span>span2</span>

方式一: dispaly:flex

.container {
    background-color: aqua;
    display: flex;
  }

dispaly:flex.png

方式二: dispaly:inline-flex

    .container {
        background-color: aqua;
        display: inline-flex;
      }

截屏2023-11-08 15.33.51.png

可以看到 dispaly 为flex 时,容器是块级元素,宽度默认占一行,与块级元素特征一致.

为inline-flex时,容器是行内块级元素,其宽度由其内部的元素的宽度的总和.

另外也可以看出来,被设置成容器的html元素,依然处于标准流中,并没有脱标.

容器的伸缩性

当不设置 容器的宽度和高度时,容器时一定能保障,在容器内的flex-item 是在容器内的,因为容器是包裹其内的flex-item的,此时容器的宽高会伸缩.

当设置 容器的宽度和高度时,容器的宽度和高度就是固定的,不会伸缩,不保证 能包含其内的所有flex-item. 如果要保障所有元素 都在容器内,请设置换行:flex-wrap:wrap

.container {
    background-color: aqua;
    /* width: 800px; */
    /* height: 150px; */
    width: 150px;
    height: 100px;
    /* max-width: 150px; */
    display: flex;
    /* overflow: scroll; */
    /* flex-wrap: wrap; */

    /* flex-direction: column; */
    /* flex-direction: row; */
  }
  .item {
    width: 50px;
    height: 50px;
    min-height: 50px;
    min-width: 50px;
    border: 1px solid orange;
    border-radius: 5px;
    background: greenyellow;
    box-sizing: border-box;
  }
<div class="container">
  <span class="item">item1</span>
  <div class="item">item2</div>
  <i class="item">item3</i>
  <i class="item">item3</i>
  <i class="item">item3</i>
</div>

截屏2023-11-08 17.57.15.png

3.容器属性探索

需要明确说明的一点是,flex 布局是 一种一维布局方式.一维是线性的,横向或者竖向.

在一维中的区域中,通过flex 布局 我们可以为所欲为,任意布局,但在处理多行布局时,此时的布局已经是二维的了,flex 布局处理起来有很多地方是差强人意的,这不是你用的不好,也不是flex布局不行,而是flex 布局的出现,本身就不是针对这种场景的.

如果想更好的处理二维 布局的问题,可以使用更强大的布局方式 grid 布局,但是兼容性是一大考验. 比较好的方式是通过flex 布局与标准流,浮动,定位等布局方式相结合,达到更好的效果.

单行布局探索

flex-direction

  • flex-direction 决定了flex 布局的主轴方向,可以是从左到右,从右向左,从上到下,从下到上.
  • 同时,flex-dirction 也决定了 交叉轴的方向,
    • 主轴方向为水平(row,row-reserve)时,交叉轴方向为从上到下,
    • 主轴方向为竖向时(column, column-reserve),交叉轴方向为水平从左到右

justify-content

  • justify-content 决定了 主轴方向上的 对齐方式.

align-items align-items 决定了单行 flex items 在交叉轴的上的 对齐方式.

1. 设置flex-direction 改变主轴方向,默认flex-direction 是row ,代表横向,排布,如果我们将其设置为column 就变成了竖向排布.

.container {
    background-color: aqua;
    width: 200px;
    height: 80px;
    display: flex;
    /* flex-direction: row; */
}
.item {
    width: 50px;
    height: 50px;
    /* min-height: 50px;
    min-width: 50px; */
    border: 1px solid orange;
    border-radius: 5px;
    background: greenyellow;
    box-sizing: border-box;
}
<div class="container">
  <span class="item">item1</span>
  <div class="item">item2</div>
  <i class="item">item3</i>
</div>

截屏2023-11-09 16.56.30.png

修改 flex-direction 为column,

flex-direction: column

截屏2023-11-09 16.58.30.png

可以看到,flex item 的高度进行了收缩,以适应容器的高度,这是因为 flex-items 中 flex-shrink 的属性设置值决定的,如果不想让 flex item 收缩,可以设置 flex-item的flex-basis 或者 flex-item 的min-height,两者都可以 改变flex item 的 高度,使其不收缩,因为 这两者的优先级都高于 width,height 属性. 但是这也可能会导致 flex -item 超出其容器的范围,请综合考虑使用.

一个flex item size的 几种设置属性的 优先级高低

  • max-width\max-height\min-width\min-height
  • flex-basis
  • width\height
  • 内容本身的 size

2.修改justify-content 属性,改变 主轴方向上的 对齐方式.

justify-content: space-around.

flex-firection: row.

截屏2023-11-09 17.12.53.png

3. 修改align-items,改变交叉轴方向上的对齐方式

algin-items:flex-end.

截屏2023-11-09 17.15.31.png

4. 修改主轴方向, 再看 主轴 和交叉轴的 对齐 方式,理解主轴与交叉轴的概念

flex-direction:column;

align-items:flex-end;

截屏2023-11-09 17.58.45.png

多行 布局探索

多行布局主要是实践 flex-wrapalign-content 这两个属性.

flex-wrap

flex-wrap 的作用很简单,就是将主轴上放不下的flex item 进行换行,不至于超出容器的宽度. 这里附上代码

.container {
    background-color: aqua;
    width: 200px;
    height: 120px;
    display: flex;
    flex-wrap: wrap;
    flex-direction: row;
    justify-content: flex-start;
    align-items: flex-start;
    /* align-content: stretch; */
  }
  .item {
    width: 60px;
    height: 60px;
    /* min-height: 50px;
    min-width: 50px; */
    border: 1px solid orange;
    border-radius: 5px;
    background: greenyellow;
    box-sizing: border-box;
  }
<div class="container">
  <span class="item">item1</span>
  <div class="item">item2</div>
  <i class="item">item3</i>
  <span class="item">item4</span>
</div>

截屏2023-11-09 19.44.26.png

当容器的高度 超出两行的高度,并且flex-item的数量 不足3 行 时,flex 布局的显示会显得很奇怪. 比如我调整容器宽度为 200px 时,

截屏2023-11-09 19.47.49.png

此时 我们发现 item4 并没有 紧挨着item1,这是什么原因呢, 这里就涉及到容器的另外一个属性,align-content,虽然我们没有设置这个属性,但是它是有默认值的,默认值是stretch,看过概念篇的,stretch 值 会将 对应轴上的所有空间 都拉满.

而align-content 是以行为单位的,那么其实现在 交叉轴上 只有 两行,也就是有两个元素,由于两个元素的高度是固定的,而且无法沾满整个交叉轴的空间(60*2 = 120 < 200),而stretch的概念又要占满整个空间,此时怎么办呢?

此时 flex 布局会将 交叉轴 剩余的高度60,平均分给每一个元素,以此来保证stretch的概念,元素占满整个轴的空间.

至于这样做对不对,仁者见仁,智者见智,毕竟flex 布局只是 一种一维 布局方式,何必要求那么多呢.

align-content align-conetnt 主要要理解以下两个点:

  • algin-content 是以 主轴 的一行 作为单位 来 设置 对齐方式的.
  • align-conetnt 是在 多行情况下的 设置 交叉轴上的对齐方式的.

理解了这两个点就能更好的理解这个属性.

align-conetnt: center

.container {
    background-color: aqua;
    width: 200px;
    height: 200px;
    display: flex;
    flex-wrap: wrap;
    flex-direction: row;
    justify-content: flex-start;
    align-content: center;
  }
  .item {
    width: 60px;
    height: 60px;
    /* min-height: 50px;
    min-width: 50px; */
    border: 1px solid orange;
    border-radius: 5px;
    background: greenyellow;
    box-sizing: border-box;
  }
<div class="container">
      <span class="item">item1</span>
      <div class="item">item2</div>
      <i class="item">item3</i>
      <span class="item">item4</span>
      <div class="item">item5</div>
      <i class="item">item6</i>
      <span class="item">item7</span>
      <div class="item">item8</div>
      <i class="item">item9</i>
    </div>

截屏2023-11-09 20.16.06.png

algin-conetent:space-between

截屏2023-11-09 20.17.32.png

flex-items 探索

flex-item 的类型特点

在概念篇我们 有提到,flex-item 不再严格区分块级元素和行内级元素,默认情况下是包裹内容,所有的flex-item 都被视为 行内级块级元素,我们可以设置它的宽度和高度,哪怕它本身是inline 元素.而 所有元素也会在同一行显示,那么它是块级元素

.container {
    background-color: aqua;
    display: inline-flex;
  }
<div class="container">
      <span>item1</span>
      <div>item2</div>
      <i>item3</i>
</div>

截屏2023-11-08 16.06.42.png

flex-items 的属性探索

1. order

可以通过设置某个 flex item 的order 属性,来改变 item 在 布局中的位置,值越小就越靠前

为 item3 添加 order 属性,可以看到 它的位置发生了变化

.container {
    background-color: aqua;
    width: 200px;
    height: 100px;
    display: flex;
    flex-wrap: wrap;
    flex-direction: row;
    justify-content: space-around;
  }
  .item {
    width: 60px;
    height: 60px;
    /* min-height: 50px;
    min-width: 50px; */
    border: 1px solid orange;
    border-radius: 5px;
    background: greenyellow;
    box-sizing: border-box;
  }
  .item-order {
    order: -1;
  }
<div class="container">
  <span class="item">item1</span>
  <div class="item">item2</div>
  <i class="item item-order">item3</i>
</div>

截屏2023-11-09 20.29.36.png

2.align-self

给 某个 flex item 设置 align-self,可以在这个flex item 上覆盖容器上设置的 align items,

简单说设置 align 可以单独设置 这个 flex item 在 交叉轴上的 对齐 方式.

.container {
    background-color: aqua;
    width: 200px;
    height: 100px;
    display: flex;
    flex-direction: row;
    justify-content: space-around;
    align-items: flex-start;
  }
  .item {
    width: 60px;
    height: 60px;
    border: 1px solid orange;
    border-radius: 5px;
    background: greenyellow;
    box-sizing: border-box;
  }
  .item-order {
    order: -1;
  }
  .item-self {
    align-self: center;
  }
<div class="container">
  <span class="item">item1</span>
  <div class="item item-self">item2</div>
  <i class="item">item3</i>
</div>

截屏2023-11-09 20.37.52.png

3.flex-grow

flex-grow 决定了 flex items 如何拓展 (拉伸/成长)

可以设置任意 非负数字( 正小数,正整数,0),默认值是 0

flex-grow 的使用 有一个 前提条件,就是 容器在 主轴方向有 剩余 size是, flex-grow 才有效.

如果 flex-items 的flex-grow 总和 sum 超过1,

  • 每个felx item 拓展的size = 容器的剩余 size * flex-grow / sum

flex itmes 拓展后的最终 size 不能 超过 max-width\max-height

先来看看 主轴上 有剩余size 时的使用

.container {
    background-color: aqua;
    width: 200px;
    height: 100px;
    display: flex;
    flex-direction: row;
    justify-content: space-around;
    align-items: flex-start;
  }
  .item {
    width: 60px;
    height: 60px;
    border: 1px solid orange;
    border-radius: 5px;
    background: greenyellow;
    box-sizing: border-box;
  }
  .flex-grow {
    flex-grow: 1;
  }
<div class="container">
  <span class="item">item1</span>
  <div class="item flex-grow">item2</div>
  <i class="item">item3</i>
</div>

截屏2023-11-10 10.33.00.png 可以 看到 item2 在主轴方向(水平方向)进行了拉伸.

但如果我们给 item2 设置上max-width 与width 相等,它就不会再生长.

.flex-grow {
    flex-grow: 1;
    max-width: 60px;
 }

截屏2023-11-10 10.36.22.png

如果 把 item1 和 item2 都加上 flex-grow:1, 那么 item1,item2 会平分剩余空间

.flex-grow {
    flex-grow: 1;
 }
<div class="container">
  <span class="item flex-grow">item1</span>
  <div class="item flex-grow">item2</div>
  <i class="item">item3</i>
</div>

截屏2023-11-10 10.44.05.png

4.flex-shrink

flex-shrink 决定了 flex itmems。如何收缩(缩小)

  • 可以设置任意非负数字(正小数,正整数,0),默认值 是 1
  • 当flex items 在 主轴 方向 上超过了 容器的size ,flex-shrink 属性才会有效 如果 所有 flex itms 的 flex-shrink 总和 超过 1,
  • 每个felx item 收缩的size = flex items 超出容器的 size * 收缩比例 / 所有的 felx itmes的收缩比例 之和.

flex items 收缩后的最终size 不能小于 min-width\min-height

.container {
    background-color: aqua;
    width: 200px;
    height: 100px;
    display: flex;
    flex-direction: row;
    justify-content: space-around;
    align-items: flex-start;
  }
  .item {
    width: 100px;
    height: 100px;
    border: 1px solid orange;
    border-radius: 5px;
    background: greenyellow;
    box-sizing: border-box;
  }
   .flex-shrink {
    flex-shrink: 3;
  }
<div class="container">
  <span class="item flex-shrink">item1</span>
  <div class="item flex-shrink">item2</div>
  <i class="item">item3</i>
</div>

截屏2023-11-10 17.02.22.png

5.flex-basis

**flex-basis 用来设置 flex itmes 在 主轴方向上的 base size **

  • auto 默认值,具体所占的数值(100px)

决定 flex items 最终 base size 的因素,从优先级高到低

  • max-width\max-height\min-width\min-height
  • flex-basis
  • width\height
  • 内容本身的 size
.container {
    background-color: aqua;
    width: 200px;
    height: 500px;
    display: flex;
    flex-direction: column;
    justify-content: space-around;
    align-items: flex-start;
  }
  .item {
    width: 100px;
    height: 100px;
    border: 1px solid orange;
    border-radius: 5px;
    background: greenyellow;
    box-sizing: border-box;
  }
    .flex-basis {
    flex-basis: 140px;
  }
<div class="container">
  <span class="item flex-basis">item1</span>
  <div class="item">item2</div>
  <i class="item">item3</i>
</div>

截屏2023-11-10 17.21.27.png

6.flex

flex 只是 flex-grow || flex-shrink || flex-basis 的简写,具体用法请参考**概念篇**的使用

概念篇 遗留 问题解决

先来描述问题

截屏2023-11-10 17.41.21.png

问题是 当使用 多行排列时,当第二行的元素 少于第一行的元素, 第二行的flex item 不能和第一行的元素 上下对齐.

这里提供一种利用替补元素的方式解决问题. 通过给剩余位置补充等宽的i元素(但不设置高度),来达到布局的目的.

.container {
    background-color: aqua;
    width: 200px;
    height: 200px;
    display: flex;
    flex-direction: row;
    flex-wrap: wrap;
    justify-content: space-around;
    align-items: flex-start;
  }
  .item {
    width: 50px;
    height: 50px;
    border: 1px solid orange;
    border-radius: 5px;
    background: greenyellow;
    box-sizing: border-box;
  }
  .container > i {
    width: 50px;
  }
<div class="container">
  <span class="item">item1</span>
  <div class="item">item2</div>
  <i class="item">item3</i>
  <div class="item">item4</div>
  <i class="item">item5</i>
  <i class="item">item6</i>
  <i class="item">item7</i>
  <i class="item">item8</i>
  <i class="item">item9</i>
  <i class="item">item10</i>
  <i class="item">item11</i>
  <i class="item">item12</i>
  <i class="item">item13</i>
  <i></i><i></i><i></i>
</div>

截屏2023-11-10 19.20.14.png