阅读视图

发现新文章,点击刷新页面。

Flexbox 布局实际用例

上篇文章介绍了 flexbox 的属性与示例,本文再通过几个 flex 布局的案例来体会 flex 布局的特性带来的便利和问题~

格式化上下文

当我们给父容器设置 flex 属性后,flex 容器会在容器内创建一个新的 **flex 格式化上下文(formatting context)**。在这上下文中 floatclear 将失去作用,vertical-align 对于 flex 元素也不再会起作用。

在实际开发中,当我们使用行内元素(inlineinline-block) 时,有时候可能会看到元素之间会有一个奇怪的间隙,并且设置的字体越大间隙就越大。原来这个间隙是我们在编写源代码时标签换行导致,不换行就不会出现这种情况。

多数情况下,我们在编写代码时会习惯用编辑器对代码进行格式化,格式化后会使这些标签换行从而导致间隙。这在要求像素级还原的项目中就有点尴尬了。

以前常见的做法是在父元素设置 font-size: 0 消除间隙,再设置子元素的字体大小。这样做确实有点麻烦,因此在 flex 上下文中,这些间隙默认就会被清除。

圣杯布局

通常我们使用 flex 布局更多的是用于整体的布局设计,如:

在互联网早期,由于用户网路的限制,经常会出现 html 的内容显示出来但页面样式还没加载出来的情况,这会导致用户没能最先看到想看的东西。因此 Matthew Levine 在 2006 年提出了圣杯布局的概念,在 HTML 源代码中将用户想看的内容挪到次要内容的前面。

上例 demo 就是使用 flex 布局实现的圣杯布局,虽然在 HTML 源码里 Main 处于其他两块内容之上,但通过 order 属性可以调整元素间的顺序。

除此之外,还可以通过媒体查询(@media)做响应式页面,当屏幕宽度小于 640px 后仅需修改几项 flex 属性就可以改变布局排列的方式,十分灵活。

如果你使用过 react/vue 主流 UI 库的话,你就会发现他们使用布局容器也是 flex 布局实现的,比如 Element UIAnt Design 等。

栅格布局

栅格布局也可以通过 flex 来实现:在以下的 demo 中,HTML 源码内的各元素都是平级,通过调整 flex 属性实现了跨行或跨列的效果。

justify-content 尾列不整齐

让CSS flex布局最后一行列表左对齐的N种方法 –By 张鑫旭

多数情况下使用 justify-content 是要求子元素们散开,但尾列元素不够的时候,散开就显得很奇怪了,为此我们可以做如下处理:

动画

在 MDN Animatable CSS properties 上列出了可以使用 AnimationsTransitions 进行动画处理的属性,其中就有 flex 属性。因此还可以结合动画进行布局设计:

结束

通过以上几个案例是不是对 flex 布局的灵活有了更深的感受呢?以上 demo 大多借鉴已有的思路,如果你有什么好的想法,也可以自己动手尝试一番或分享出来~

参考资料:

Pixiv 背景图例:

  1. ちょけ | アリスミクと白うさぎ
  2. Azit | Miku
  3. ぽむ | もっと高くまで!
  4. 雨陌 | 8.31
  5. akino | つもりつもるキモチ。

Flexbox 布局入门

互联网早期实现布局是需要通过多种不同属性组合才能实现我们想要的布局。

比如常见的垂直居中,刚接触 css 的朋友看到 vertical-align: middle; 这个属性可能就会认为它就是用于垂直居中的,但实际上并没有那么简单。如果想要通过该属性来实现垂直居中,还需要其他小伙伴配合。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
.container {
width: 200px;
height: 200px;
border-radius: 6px;
text-align: center;
color: #fff;
background: #e44b27;
white-space: nowrap;
}

/* 该伪类是实现垂直居中关键 */
.container:after {
content: '';
display: inline-block;
height: 100%;
vertical-align: middle;
}
.content {
display: inline-block;
white-space: normal;
vertical-align: middle;
text-align: left;
}
1
2
3
<div class="container">
<div class="content">我想居中!</div>
</div>

这样看来,为了实现垂直居中布局,我们还得打一套组合拳才能出来才行,是不是看起来有点麻烦的样子?

W3C 在 2009 年提出的 Fiexbox(flex) 布局草案,就是针对用户界面设计优化的 CSS 盒模型。如果使用 flex 布局来实现上面的垂直居中布局的话,可以简化为:

1
2
3
4
5
6
7
8
9
10
11
12
.container {
width: 200px;
height: 200px;
border-radius: 6px;
color: #fff;
background: #e44b27;

/* 使用 flex 布局 */
display: flex;
justify-content: center;
align-items: center;
}
1
2
3
<div class="container">
<div>我想居中!</div>
</div>

修改后的代码就显得更精简了,也不需要其他小伙伴来搭把手。布局的事情就让 flex 家族自己来解决即可。


概念

应用 flex 布局的容器我们通常称为 **弹性盒子/容器(flex container)**。弹性容器可以由 display: flexdisplay: inline-flex 生成。弹性盒子的子项常称为 **弹性元素/项目(flex items)**,它以 flex 布局模型进行布局。

1
2
3
.container {
display: flex | inline-flex;
}

如果想要学习 flex 布局的工作方式,最先需要学习的是它自身的术语。下面直接引用 flex 草案中术语的介绍图:

别被原版英文术语给吓倒了,咱们翻译一下其实就很好理解了:

在术语示意图中可以看到两根轴,分别是**主轴(main axis)垂直交叉轴(cross axis)。同时标注了主轴起点(main start)终点(main end)交叉轴的起点(cross start)终点(cross end)**。

默认情况下 flex 布局是按主轴的方向进行布局的。flex 元素所占据的 主轴空间(main size) 就是 flex 元素的宽度(width)、所占据的 交叉轴空间(cross size) 就是 flex 元素的高度(height)。


flex 容器属性

flex 容器里可以通过以下几种属性来控制容器的行为:

  • flex-direction
  • flex-wrap
  • flex-flow
  • justify-content
  • align-content
  • align-items

为了更好的观察各属性的行为,笔者在 codepen 上给不同属性都写了 demo 做参考。

目前有个新规范(CSS Box Alignment Module Level 3)正处于工作草案的状态中,对一些属性添加新值,如 [first|last]? baselineself-startself-endstartendleftrightunsafe | safe

这些新值多数浏览器都没实现,为了便于演示,此处仅讲解初始版本的值。Firefox 浏览器对新值实现的比较超前,也建议通过使用 Firefox 浏览器来查看 demo。

flex-direction

flex-direction 指示内部元素如何在 flex 容器中布局。可以简单的理解为 flex 容器的布局方向。其默认值为 row,可选语法如下:

1
2
/* 常用属性 */
flex-direction: row | row-reverse | column | column-reverse;
  • row: 主轴起点和主轴终点与内容方向相同。简而言之就是内容从左到右进行布局。
  • row-reverse: 与 row 行为相同,但主轴起点和主轴终点对调了位置。
  • column: 主轴由水平方向转为垂直方向,布局从上往下排。
  • column-reverse: 主轴由水平方向转为垂直方向,布局从上往下排。

值得注意的是,全局属性 dir 的作用是指示元素的文本的方向性,该属性会受到 rowrow-reverse 的影响。

flex-wrap

flex-wrap 指定 flex 元素单行显示还是多行显示 。如果可以换行,你甚至还可以通过该属性控制行的堆叠方向。它的取值如下所示:

1
flex-wrap: nowrap(默认值) | wrap | wrap-reverse;

可以通过本例 demo 右上角的按钮来修改元素的数量,观察三个值之间的变化:

  • nowrap: flex 容器宁愿压榨元素的空间也不肯换行。甚至压缩到一定地步后还会溢出容器。
  • wrap: 若子项超过容器所容纳的宽度,则允许断行展示。
  • wrap-reverse: 和 wrap 的行为一样,只是交叉轴起点与终点互换

flex-flow

flex-flow 属性是 flex-directionflex-wrap 的简写。这个没啥好说的,也就不额外写 demo 了。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
/* 语法 */
flex-flow: <flex-direction> || <flex-wrap>;

/* 单独设置 flex-direction 的属性 */
flex-flow: row;
flex-flow: column;

/* 单独设置 flex-wrap 的属性 */
flex-flow: nowrap;
flex-flow: wrap;

/* 同时设置两种属性,建议按照语法顺序进行书写 */
flex-flow: row nowrap;
flex-flow: column wrap;

justify-content

justify-content 属性定义了容器主轴中各 flex 元素之间的对齐方式。这是 flex 布局中常用的属性之一。

1
2
3
justify-content: normal |
space-between | space-around | space-evenly |
center | flex-start | flex-end

在初始版本中,justify-content 的默认值为 flex-start。但在最新版本中的 chrome 浏览器被修改为了 normal

为了对比属性之间的差异,本例 demo 将元素的两侧 margin 清空:

  • normal: 排列效果等同 flex-start
  • flex-start: 默认情况是左对齐,从行首开始排列。每行第一个 flex 元素与行首对齐,同时所有后续的 flex 元素与前一个对齐。
  • flex-end: 默认情况下是右对齐,从行尾开始排列。每行最后一个 flex 元素与行尾对齐,其他元素将与后一个对齐。
  • center: 该值使元素居中对齐。
  • space-between: 首尾两端对齐,内部元素之间的间距相等。
  • space-around: 在每行上均匀分配弹性元素。相邻元素间距离相同,首尾两个元素的距离是相邻元素之间距离的一半
  • space-evenly: 主轴内各元素两侧均匀分配剩余空间。(注意此处与 space-around 的差异)

align-items

align-items 属性除了可以在 flex 布局中有效,还可以在 grid(网格) 布局中应用。在 flex 布局中它的作用是决定交叉轴的对齐方式。这也是 flex

1
2
3
4
5
/* 主流浏览器已经实现的值 */
align-items: normal | flex-start | flex-end | center | baseline | stretch

/* 新草案添加的值 */
align-items: | start | end | [ first | last ]baseline | left | right
  • normal: 在 flex 布局中 normal 的表现效果如同 stretch 一样。
  • stretch: 弹性元素被在交叉轴轴方向被拉伸到与容器相同的高度或宽度。若容器没有设置高度,则取当前行中最高元素的高度,如本例中元素 4 是第一行中最高的元素,那第一行中的高度都被拉伸到与最高元素相同的高度。第二行中最高的元素是元素 2,因此第二行高度都取至元素 2。
  • flex-start: 元素向交叉轴起点对齐。
  • flex-end: 元素向交叉轴终点对齐。
  • center: 元素在交叉轴居中。
  • baseline: 所有元素向基线对齐。侧轴起点到元素基线距离最大的元素将会于侧轴起点对齐以确定基线。在例子中放大元素 6 的 font-size, 与 center 进行对比就能看到差异了。

align-content

justify-content 是作用于主轴上,而 align-content 则是用于定义交叉轴的对齐方式。值得注意的是,若 flex 容器内只有一根轴线,该属性将不起作用

1
2
3
4
5
/* 主流浏览器已经实现的值 */
align-content: normal | space-between | space-around | space-evenly | stretch | center | flex-start | flex-end

/* 主流浏览器多数未实现的值 */
align-content: [first|last]? baseline, start, end, left, right

父容器设置了 flex 布局后,若子元素没有设定 height 属性的话,默认会将容器内的子元素进行拉伸。

为了便于观察两者的差异,笔者在 demo 中新增一列进行对比。左列的 flex 元素使用 height 属性,右列使用 min-height 属性。同时将 flex 容器高度设置为 400px:

  • normal: 像未设置值,元素处于默认位置。
  • stretch: 拉伸所有行来填满剩余空间。剩余空间平均的分配给每一行(若某元素设置了高度,那么该值对这个元素将不会起作用)。
  • flex-start: 交叉轴起点对齐。
  • flex-end: 交叉轴终点对齐。
  • center: 交叉轴居中对齐。
  • space-between: 交叉轴两端对齐,行之间间距相等
  • space-around: 交叉轴均匀对齐,行两端间距相等
  • space-evenly: 交叉轴内各元素两侧均匀分配剩余空间。

Flex Item

Flex Container(弹性容器)的一级子元素就是 Flex item(弹性元素)。以下主要应用于 Flex item 的属性。

  • flex-basis
  • flex-grow
  • flex-shrink
  • flex
  • align-self
  • order

flex-grow

flex-grow 属性用于定义元素所占有的比例,它接受一个正整数,默认值为 0

1
2
3
4
flex-grow: <number>

/* 例子: 仅接受正数的值 */
flex-grow: 1;

flex-shrink

flex-grow 相反,flex-shrink 属性处理元素收缩的问题,默认为 1,意味着元素默认会随着容器缩小而等比例缩小。当值为 0 时则不缩放。

1
2
3
4
5
6
7
flex-shrink: <number>

/* 例子: 默认缩放 */
flex-shrink: 1;

/* 例子: 使元素不缩放 */
flex-shrink: 0;

在以下 demo 中,各 flex 项目的宽高相等。当父容器有足够的空间时,元素不需要紧衣缩食,因此 flex-shrink 也没有机会表现出它的作用。

将 flex 容器尺寸调小后可以发现,flex-shrink 的值越大,元素被压榨的空间越多。

flex-basis

flex-basis 指定了 flex 元素在主轴空间(main size)所占的初始大小。

1
flex-basis:  <'width'>

当一个元素同时被设置了 flex-basis (值为 auto 除外)和 width 属性时,flex-basis 具有更高的优先级。

W3C 鼓励使用 flex 简写属性(下一小节进行秒速)来控制灵活性,而不是直接使用 flex-basis 属性。因为简写属性 flex 可以正确地重置任何未指定的属性以适应常见的用途。

flex

flex 属性是 flex-growflex-shrinkflex-basis 的简写,规定了弹性元素如何伸缩以适应 flex 容器中的可用空间,默认值为 0 1 auto

1
flex: none | [ <'flex-grow'> <'flex-shrink'>? || <'flex-basis'> ]

flex 属性可以指定 1 个,2 个或 3 个值。

单值语法: 值必须为以下其中之一:

  • 一个无单位数(<number>): 它会被当作 <flex-grow> 的值。
  • 一个有效的宽度(width)值: 它会被当作 <flex-basis>的值。
  • 关键字 noneautoinitial

双值语法: 第一个值必须为一个无单位数,并且它会被当作 <flex-grow> 的值。第二个值必须为以下之一:

  • 一个无单位数:它会被当作 <flex-shrink> 的值。
  • 一个有效的宽度值: 它会被当作 <flex-basis> 的值。

三值语法:

  • 第一个值必须为一个无单位数,并且它会被当作 <flex-grow> 的值。
  • 第二个值必须为一个无单位数,并且它会被当作 <flex-shrink> 的值。
  • 第三个值必须为一个有效的宽度值, 并且它会被当作 <flex-basis> 的值。

这个属性没啥好演示的,其实就是之前介绍的三个属性的组合:

align-self

align-self 属性在 flex 布局中作用于单个 flex 元素上,它将控制指定元素在交叉轴上的位置。

1
2
3
4
align-self: auto | normal | stretch | center | flex-start | flex-end;

/* 多数浏览器未实现的功能 */
align-self: start | end | self-start | self-end | [first | last]? baseline;
  • auto: 设置为父元素的 align-items 值,如果该元素没有父元素的话,就设置为 stretch
  • normal: 在 flex 布局中,相当于 stretch 的效果。
  • stretch: flex 元素将会基于容器的宽和高,按照自身 margin box 的 cross-size 拉伸。
  • center: 使项目在交叉轴中居中。
  • flex-start: flex 元素会对齐到 cross-axis 的首端。
  • flex-end: flex 元素会对齐到 cross-axis 的尾端。

order

order 属性用于设置指定 flex 元素在容器中的顺序。容器中的 flex 元素按升序值排序,若值相同则按其源代码出现的顺序进行排序,默认值为 0。它接受一个整数值(integer),如 -203 等。

1
order: <integer>

我们可以操作下面的 demo 来控制元素的顺序,比如将第三项元素通过 order 在移动到第一位。


兼容性

要将学到的新东西应用到实际项目中就不得不考虑其兼容性了。通过 caniuse 我们可以看到:flex 布局经过多年的发展,主流浏览器都已经对 flex 布局基本模块都实现完毕了。

PC 端需要考虑的是要不要兼容 IE,移动端最低兼容为 ios 3.2+Android 2.1+。如果你需要开发微信小程序,那么小程序官方就推荐使用 flex 布局。

早期 flex 布局是通过 display: box; 来申明,这是使用了旧的规范,后来该值被 flex 给替换掉了。还有一些很低版本的浏览器或许还需要添加浏览器前缀才能使用 flex 布局。因此你在某处看到如下代码也不用感到奇怪,这是开发者在给布局做兼容呢:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
.flex-center {
display: -webkit-box;
display: -webkit-flex;
display: -moz-box;
display: -ms-flexbox;
display: flex;
-webkit-box-pack: center;
-webkit-justify-content: center;
-moz-box-pack: center;
-ms-flex-pack: center;
justify-content: center;
-webkit-box-align: center;
-webkit-align-items: center;
-moz-box-align: center;
-ms-flex-align: center;
align-items: center;
}

但如果要我们在开发时手动写这种兼容好像不是很靠谱,兼容又冗余。所幸现在的前端开发都会使用脚手架,这些脚手架一般都会内置 postcssautoprefix 之类的插件来帮助我们完成这些事。

还有一些朋友可能会说,我们老项目还是得要兼容 IE 8+ 呀,是不是意味着跟 flex 布局无缘了?其实不是的,github 上有一个叫 flexibilitypolyfill 可以让 IE8 + 也实现 flex 布局效果.


结束

本篇介绍了 flex 布局该如何使用、各属性的作用与效果,下一篇再详细讲讲 flex 布局在实际工作中的妙用~

参考资料:

  1. CSS Flexible Box Layout Module Level 1
  2. CSS Box Alignment Module Level 3
  3. Flex Item
❌