浅谈Flex布局

当我们说flex布局时,我们在说什么,flex布局内的元素到底是怎么对齐的,大小是怎么样的,布局方式到底有什么规律?下面这篇文章将对于这些我们概念做一些简单的解释~

1.特点

首先,flex布局是专为一维内容设计的布局模型,它擅于获取大量不同大小的项,并为这些项返回最佳布局。跟浮动布局相比,flex布局的可预测性更好,还能提供更精细的控制。可以轻松解决之前困扰很久的垂直居中和等高列问题。

基本特点:

  • 可以显示为行或列,但是如果想同时控制行列,那么估计很难办到,可以考虑使用grid布局
  • 默认是单行,但是可以换行显示
  • 在换行布局中的灵活度高
  • ......

什么是等高列问题:就是一排元素各有各的高度,如果想要实现等高,调整起来十分不方便。比如例如,侧边栏和主内容区域需要视觉上等高,但内容量不同会导致高度差异。

2. 父盒子对齐方式

将一个盒子定义为flex弹性布局容器,它还定义了内部元素的展示方式。理解flex布局要理解这么几个概念,主轴,副轴(起点以及终点),换行以及水平垂直方向对齐方式

2.1 display: flex 与 display: inline-flex

使用flex布局,首先需要创建一个flex容器,使用flex布局,首先需要使用flex格式上下文,因为需要将display属性改成display:flex或者是display:inline-flex。接下来有两个问题,当我们写display:flex的时候,是什么意思,以及写完之后,发生了什么呢?

  1. 当我们写display属性的时候,我们实际上是在写什么呢?

display实际上是用来设置元素自身渲染的方式以及内部子元素的布局方式,所以display既可以设置自己的渲染方式,也可以控制子元素的对齐方式

所以实际上我们写的display:flex (完整写法应该是block flex) 而display: inline-flex 也可以写做inline flex

html 复制代码
<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="UTF-8" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <title>Document</title>
    <style>
      .container1 {
        display: flex;
        background-color: pink;
      }
      .container2 {
        display: inline-flex;
        background-color: skyblue;
      }
    </style>
  </head>
  <body>
    <div class="container1">
      <div class="box">box1</div>
    </div>
    <span>111</span>
    <div class="container2">
      <div class="box">box2</div>
    </div>
    <span>222</span>
  </body>
</html>
  1. 写完之后,发生了什么呢?

弹性项会立即开始使用其初始值来呈现一些 Flexbox 行为。

初始值包括一些:

  • 默认一行显示
  • 不会变大填满容器吧
  • 在容器开头处对齐

2.2 主轴和交叉轴、换行

在对于父盒子的渲染设置了之后,下一步重要的就是设置如何排列子元素,那么首先理清楚这么几个概念就是主轴,交叉轴以及换行。这里不过多赘述:

  • 主轴可以设置为flex-direction:row/column/row-reverse/column-reverse
  • 与之相对的就是交叉轴
  • 使用flex-wrap控制是否换行
  • 简写 flex-grow: flex-direction flex-wrap

3.子元素宽度

内部子元素的宽度主要是使用flex属性来进行设置,这个属性也是困惑笔者很久的一个属性,接下来我就尝试对于这个属性理解的更深一点:

3.1 flex: initial

flex完整的设置是 👉 flex: flex-grow flex-shrink flex-basis

flex默认的值是initial,这个值是什么意思呢,对应上面的三个属性就是0 1 auto(不扩展,可收缩 默认宽度为auto)

html 复制代码
<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="UTF-8" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <title>Document</title>
    <style>
      .container {
        display: flex;
        background-color: pink;
        gap: 0.6rem;
      }
      .box {
        /* flex: ; */
        background-color: skyblue;
      }
    </style>
  </head>
  <body>
    <div class="container">
      <div class="box box1">box1</div>
      <div class="box box2">box2</div>
      <div class="box box3">box3</div>
      <div class="box box4">box4</div>
      <div class="box box5">box5</div>
    </div>
  </body>
</html>
  • 当前宽度有剩余,所以不需要压缩,同时flex-grow:0也不需要伸展,默认的宽度是auto。那么这里的auto到底是什么意思?

    • 如果我们给子元素设置width值,那么这个auto就会应用这个width值
    • 如果没有width值,那么会根据单词的折词规则来设置 (大概类似下图)
  • 如果说按照auto排布,当前的宽度不仅没有富余,还不够,那么又会如何处理呢?下面将结合具体的计算过程说明:

    • 容器宽度: 300px
    • 子元素宽度:200 + 100 + 100 + 100 + 100 = 600px
    • 间隔:4 * 10 = 40px
    • 实际总宽度:640px
    • 👉 需要收缩的总空间 : 640 - 300 = 340 px
    • 👉 总收缩权重:200 * 1(flex-shrink) + 100 * 1 * 4 = 600
    • 最终宽度 = auto值 - 子元素收缩权重 / 总收缩权重 * 需要收缩的总空间
    • 用box1举例:200 - 200 / 600 * 340 = 86.66666666666667
    html 复制代码
    <!DOCTYPE html>
    <html lang="en">
      <head>
        <meta charset="UTF-8" />
        <meta name="viewport" content="width=device-width, initial-scale=1.0" />
        <title>Document</title>
        <style>
          .container {
            display: flex;
            background-color: pink;
            width: 300px;
            gap: 10px;
          }
          .box {
            background-color: skyblue;
          }
          .box1 {
            width: 200px;
          }
          .box2,
          .box3,
          .box4,
          .box5 {
            width: 100px;
          }
        </style>
      </head>
      <body>
        <div class="container">
          <div class="box box1">box1</div>
          <div class="box box2">box2</div>
          <div class="box box3">box3</div>
          <div class="box box4">box4</div>
          <div class="box box5">box5</div>
        </div>
      </body>
    </html>

可以看到实际情况和我们计算的结果一致,所以可以得出结论最终的宽度,是在flex-basis的基础上,综合flex-grow和flex-shrink计算得到的。

注意:启用换行后,子元素不再根据flex-shrink值收缩,任何超过弹性容器的子元素都会换行显示。

3.2 flex: auto

flex也可以设置别的值,如果flex:auto,实际上就相当于1 1 auto,可以看到和flex默认值最大的区别就是flex-grow从0变成了1

首先明确一点实际宽度不够,那么表现形式和上面是一样的,主要区别是有剩余空间的时候。之前是有剩余空间也不会扩展,现在是会扩展剩余空间,举个例子:

  • 容器宽度: 300px
  • 子元素宽度:40 + 50 * 4 = 240px
  • 间隔:4 * 10 = 40px
  • 实际总宽度:280px
  • 👉 需要伸展的总空间 : 300 - 280 = 20 px
  • 👉 总收缩权重:40 * 1(flex-grow) + 50 * 1 * 4 = 240
  • 最终宽度 = auto值 + 子元素伸展权重 / 总伸展权重 * 需要伸展的总空间

用box1举例:40 + 40 / 240 * 20 = 43.3333

计算逻辑基本上和上面完全一致,当我们打开控制台可以看到一个向右拉伸的箭头,就表示伸展的宽度

3.3 flex: 1

可以看到上面的情况下,元素的宽度各不相同,因为它们都会基于flex-basis进行计算,那么如果我们希望所有项的宽度都均等分配呢?这么时候flex: 1就派上了用场,它的三个值分别是 1 1 0,就是默认大小都为0, 平分剩余的空间

在一行定宽,一行自适应中我们这样写:

html 复制代码
<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="UTF-8" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <title>Document</title>
    <style>
      .container {
        display: flex;
        background-color: pink;
        width: 300px;
        gap: 10px;
      }
      .box {
        background-color: skyblue;
      }
      .box1 {
        width: 50px;
      }
      .box2 {
        flex: 1;
      }
    </style>
  </head>
  <body>
    <div class="container">
      <div class="box box1">box1</div>
      <div class="box box2">box2</div>
    </div>
  </body>
</html>

👉 同理类比:flex: 2其实也就是flex: 2 1 0%

3.4 flex: none

它会提供不会伸缩或收缩的不可变 flex 项。如果只是使用 flexbox 来访问对齐属性,但不希望任何灵活的行为,这可能会很有用。(使用情况少,不过多赘述)

下面的这个图大概可以展示上面的逻辑,可以和上面的内容对照理解:

4.子元素排布方式

在理解了元素的宽度如何设置了之后,接下来我们思考如何有规律的排列这些元素呢?

4.1 justify-content

当子元素未填满容器时,justify-content属性控制子元素沿主轴方向的间距。它的值包括几个关键字:flex-start、flex-end、center、space-between以及space-around.

  • 当子元素超过容器的时候,实际上这个值就不生效了

  • justify-content 的作用是分配未被 flex-grow 或主轴 margin: auto 消耗的剩余空间。如果存在以下任一情况:

    • 任意子元素 flex-grow > 0(会优先扩展并消耗剩余空间);
    • 任意子元素主轴方向 margin: auto(会优先吸收剩余空间);
    • 则剩余空间会被这两种机制提前消耗,justify-content 无法再分配空间,因此 "失效"(不生效)。

(这里如何理解呢,我们举个例子解释一下⑧)

html 复制代码
<style>
  .container {
    display: flex;
    width: 500px;
    height: 100px;
    background: #f0f0f0;
    justify-content: center;
    gap: 10px;
  }
  .item {
    width: 100px;
    height: 50px;
    background: skyblue;
    /* flex-grow: 1; */
    /* margin-left: auto; 情况2:取消注释后 justify-content 失效 */
  }
</style>
<div class="container">
  <div class="item">Item</div>
  <div class="item">Item</div>
</div>

那么这里的margin-left: auto 是什么意思,或者在定位的时候,我们经常写的margin: auto到底在写啥呢?推荐看Everything About Auto in CSS 这篇文章,这里简单说一下就是:

比如div p等元素默认的width:auto, 就是占满父元素的宽度。但是height: auto就是实际文本的内容(为什么有这样的差异,实际上我们基本上只控制width,在垂直方向上习惯于让元素自然排布,这也就是为什么会出现前面说的等高列问题)

margin比较常见的用法就是使用margin: 0 auto(水平居中),以及在定位布局以及flex等布局中提供了一种对于元素更加精细的控制方式,比如这里的margin-left: auto

在这里我们给item单独设置了margin-left:auto, 可以看出它的表现形式完全不受justify-content的控制了,则将该方向的剩余空间全部分配给该 margin

详细可以看这个:W3C Flexbox 规范

4.2 align-items

align-items则控制子元素在副轴方向的对齐方式。align-items的初始值为stretch,在水平排列的情况下让所有子元素填充容器的高度。在垂直排列的情况下让子元素填充容器的宽度,因此它能实现等高列

注意: 取值是baseline的时候可以实现不同字号的内容对齐

4.3 align-content

如果开启了换行(用flex-wrap), align-content属性就可以控制弹性容器内沿副轴方向每行之间的间距,初始值stretch

4.4 align-self 以及 order

align-self 它能单独给弹性子元素设定不同的对齐方式

使用order属性能改变子元素排列的顺序

5.小记

实际上很早之前就接触到了这个属性,对于其中的细节一直比较模糊,但是平时又会经常遇到,自己每次简单记一下就觉得理解了,实际上还差的很远。这篇也是简单梳理,很多细节也不太详尽,如果有不足错误欢迎批评指正~

参考资料

相关推荐
魔云连洲3 小时前
详细解释浏览器是如何渲染页面的?
前端·css·浏览器渲染
Kx…………4 小时前
Day2—3:前端项目uniapp壁纸实战
前端·css·学习·uni-app·html
培根芝士5 小时前
Electron打包支持多语言
前端·javascript·electron
mr_cmx6 小时前
Nodejs数据库单一连接模式和连接池模式的概述及写法
前端·数据库·node.js
东部欧安时6 小时前
研一自救指南 - 07. CSS面向面试学习
前端·css
涵信6 小时前
第十二节:原理深挖-React Fiber架构核心思想
前端·react.js·架构
ohMyGod_1236 小时前
React-useRef
前端·javascript·react.js
每一天,每一步6 小时前
AI语音助手 React 组件使用js-audio-recorder实现,将获取到的语音转成base64发送给后端,后端接口返回文本内容
前端·javascript·react.js
上趣工作室6 小时前
vue3专题1------父组件中更改子组件的属性
前端·javascript·vue.js
冯诺一没有曼7 小时前
无线网络入侵检测系统实战 | 基于React+Python的可视化安全平台开发详解
前端·安全·react.js