..

Flex 布局

概念

Flex 布局是在水平与垂直两个 有向轴 上描述子元素的布局行为。它主要描述的是子元素在单列(或单行)上的单维布局,更复杂的两维度网格布局使用 Grid 布局会更好描述一点。

它描述了子元素布局的种类主要有如下几种:

  • 在轴上的 位置。是在轴的起始、中间、末尾位置。
  • 剩余轴空间的分配。是分配剩余空间,还是按某个比例撑满剩余空间?
  • 元素在整个 Flex 容器顺序。
  • 元素在 “Line Box” 里的排列方式。是相对 “Line Box” 交叉方向的起始、中间、末尾对齐,还是撑满。

有向轴的方向与 Flex 容器的文本书写方向有关,这个其实比较复杂。大部分情况下,我们都是字之间从左往右排列,行之间自上而下排列的书写方向,所以一般以这个为默认场景来说明 Flex 轴的方向。

  • 当轴是水平轴时,其默认方向是从左往右的;
  • 当轴是垂直轴时,其默认方向是从上往下的;

举几个例子展开说明一下。

flex-direction 这个属性定义的是主轴的方向。它的默认值是 row,也就是说默认主轴是水平方向的轴,那么其方向就是从左往右(因为默认文本方向是从左往右,后续不再解释)。因为主轴是水平轴,所以副轴就是垂直轴,那么其方向就是从上往下(同样是因为默认文本行是自上而下怕列,后续不再解释)。

Flex 只提供了修改主轴方向的属性。当 flex-direction 的值为 row-reverse 时,其含义就是主轴的方向与文本水平排列方向相反。那么在默认场景下,它的方向就变成从右往左。而副轴依然是垂直轴,其方向依然只能是从上往下。

当主轴变为垂直轴时,如 flex-direction: column,那么其方向就是从上往下,而副轴则是水平轴,其方向就是从左往右。当 flex-direction: column-reverse 时,主轴的方向就是从下往上,副轴方向不变。

align-content与align-items

我们日常用得最多的,其实是 align-items。在我第一次接触 Flex 布局的时候,我就经常在想:“为什么控制元素在主轴行为的属性叫 justify-content,而控制元素在副轴行为的属性却叫 align-items 而不是 align-content 呢?

实际上,控制元素在副轴行为的属性就是 align-content

align-items 控制的是在一个 “Line Box”(姑且这么叫吧,想象 flex-wrap: wrap 的场景下,换成多行,每一行就是一个 “Line Box”)内,元素在这个 “Line Box” 内副轴方向上的排列属性。

那么 align-content 控制的就是这些 “Line Box” 在副轴方向上的排列属性。

我们通过构建一个例子来说明一下:

<!-- pg-title: align-items 与 align-content -->

<div class="container">
    <!-- 第1个 “Line Box” -->
    <div class="normal">Normal</div>
    <div class="tall">Tall</div>
    <div class="low">Low</div>

    <!-- 第2个 “Line Box”,特意用 align-self 修改了几个元素 -->
    <div class="normal" style="align-self: center;">Normal</div>
    <div class="tall">Tall</div>
    <div class="low" style="align-self: flex-start;">Low</div>

    <!-- 第3个 “Line Box” -->
    <div class="normal">Normal</div>
    <div class="normal">Normal</div>
    <div class="normal">Normal</div>

    <!-- 第4个 “Line Box” -->
    <div class="normal">Normal</div>
    <div class="normal">Normal</div>
</div>
.container {
    box-sizing: border;
    margin: 20px;
    border: 1px solid black;
    width: 399px;
    height: 400px;

    display: flex;
    flex-wrap: wrap;
    justify-content: space-around;
    align-content: flex-start;
    align-items: flex-end;
}

.container > div {
    width: 100px;
    border: 1px solid blue;
}
.tall {
    height: 100px;
}
.low {
    height: 20px;
}

上面的例子,刻意制造了一个宽度 399px 的 Flex 容器,每个子元素固定宽度为 100px,这样每排最多只能容纳 3 个元素,从而制造出了 4 个 “Line Box”,这些 “Line Box“ 都是我虚构出来的,在 HTML 里加了备注。

试着调整下 align-contentalign-items 的属性。你就会理解,align-content 控制的对象是 “Line Box” 而不是直接控制子元素。

不过实际情况很少用得这么复杂。实际上,有这种需求的时候转而用 Grid 布局或许更好一些。