Flexbox 是一种 CSS 布局机制,可以说是目前浏览器原生支持的最好、使用最广泛的布局机制了。本文通过一些例子来说明 Flexbox 布局的工作原理,可以让我们更好的使用 Flexbox。

与 CSS Grid 可以同时在横向和纵向两个方向进行布局不同,Flexbox 只能在单一方向上进行布局,即要么横向,要么纵向。所谓布局,其实就是空间的分配过程,也就是说计算元素尺寸和容器剩余空间尺寸的过程。

Flexbox 的布局原理

整个布局过程我们可以简单的总结如下:

  1. 计算 flex 容器内的可用空间。整个容器的尺寸减去容器的 border、padding 等所得的剩余空间尺寸。
  2. 计算每个 flex 元素的 flex base 尺寸和元素的假设尺寸。具体计算方法是取 flex-basis、min-width 和 flex 元素内容尺寸的较大者。flex base 尺寸是 flex 元素需要的最小尺寸,这个尺寸不能小于元素内容的尺寸。元素的假设尺寸是指在 flex 因子生效前元素的尺寸,flex 因子生效后可能导致元素发生伸缩。
  3. 计算容器内所有 flex 元素的假设尺寸总和。
  4. 将所有元素的假设尺寸总和与容器内可用空间尺寸做比较,来确定 flex 因子,也就是说当假设尺寸总和超过容器内可用空间尺寸时,使用 flex-shrink,否则使用 flex-grow。在同一时间,flex-shrink 和 flex-grow 只有一个生效。

所谓 flex 因子,简单来说就是缩小和放大。浏览器在进行 flexbox 布局时会先确定使用哪种 flex 因子,然后再根据选用的 flex 因子来对元素尺寸进行调整。

在进行调整的时候,就会涉及到一个剩余空间的计算问题。如果 flex 元素明确指定了尺寸大小(definite size,比如设置了 width),那么这个元素就是不可伸缩的。如果没有显式指定尺寸,则会按照上面第2步那样计算假设尺寸。剩余空间的尺寸就是容器内的可用空间尺寸减去这些元素的尺寸之和。

例子说明

我们有如下 dom 结构:

<div id="flex">
<div id="a">Antidisestablishmentarianism</div>
<div id="b">B</div>
<div id="c">Cherries jubilee</div>
<div id="d">D</div>
<div id="e">E</div>
</div>

样式如下:

[id=flex] {
font-weight: 300;
display: flex;
outline: 1px dashed #555;
width: 1200px;
margin: 3rem auto;
}
/*其他样式已省略*/

页面展示效果如下:

我们没有设置元素的 flex 样式属性,默认值是 0 1 autoflex 样式属性是 flex-growflex-shrinkflex-basis 这三个样式属性的简写形式。0 1 auto 分别对应为 flex-growflex-shrinkflex-basis 的值。

通过取值可以看到,因为我们禁止了放大和收缩,并且 flex-basis 的值是 auto,浏览器就使用元素的最大内容尺寸来计算所有元素的尺寸总和,比容器的尺寸(1200px)小,所有会有额外的剩余空间。

关于 flex 这个样式属性,我们额外做一些说明。flex 属性可以接收最少一个、最多三个属性值。

当只有一个属性值的时候,flex 的工作模式是这样的 <number> 1 0。即 flex: 2 最终的结果是 flex: 2 1 0

当有两个属性值的时候,第一个值会被解析为 flex-grow,第二个值如果是数字,则会被解析为 flex-shrink,如果是一个合法的宽度值则会被解析为 flex-basis。即 flex: 1 0 解析为 flex: 1 0 0,而 flex: 1 20rem 则被解析为 flex: 1 1 20rem

当有三个属性值的时候,第一个值被解析为 flex-grow,第二个值必须为数字,且会被解析为 flex-shrink,第三个值则必须是合法的宽度值,被解析为 flex-basis

设置 flex-grow

我们增加如下样式设置:

[id=flex] > div {
flex: 1;
}

实际上等同于 flex: 1 1 0,即 flex-grow: 1。从前面的例子我们可以看到,所有元素的假设尺寸之和是小于容器剩余空间尺寸的,所以浏览器会使用 flex-grow 来作为 flex 因子,因为我们设置了 flex-grow: 1 ,因此浏览器会等比放大所有的元素。如下图:

实际上浏览器会循环通过下面的公式来计算每个元素的最终尺寸:

当前元素伸缩值 = (当前剩余空间 - 所有剩余元素的 flex-grow 值的和) * 当前元素的 flex-grow 值

在上面的例子中,容器的剩余空间为 1200px,因此,通过公式计算:

( 1200 ÷ ( 1 + 1 + 1 + 1 + 1 ) ) × 1 = 240

元素 A 的最终尺寸为 240px + flex-basis = 240px + 0 = 240px。但是,因为 Antidisestablishmentarianism 这个单词比较长,实际会占用 417px 的空间大小,因此元素 A 的最终尺寸为 417px

此时,在计算元素 B 的尺寸时,剩余空间为 1200px - 417px = 783px。元素 B 的伸缩值为:

( 783 ÷ ( 1 + 1 + 1 + 1 ) ) × 1 = 195.75 0 + 195.75 = 195.75

同理,元素 C、D、E 的伸缩值分别为:

C: ( 587 ÷ ( 1 + 1 + 1 ) ) × 1 ) = 195.67
D: ( 391.33 ÷ ( 1 + 1 ) ) × 1 = 195.665
E: ( 391 - 195.665 ÷ 1 ) × 1 = 195.335

总结来说,浏览器从剩余空间中减去已经分配的空间,然后计算下一个 flex 元素的伸缩值和最终大小。

差异化的 flex-grow

我们修改一下元素的 flex-grow 值:

div > :not([id=c]) {
flex: 1;
} [id=c] {
flex: 5;
}

此时,页面展示效果如下:

各元素的伸缩值计算如下:

A: 417px
B: ( 783 ÷ ( 1 + 1 + 5 + 1 ) ) × 1 = 98
C: ( 685 ÷ ( 5 + 1 + 1 ) ) × 5 = 490
D: ( 195 ÷ ( 1 + 1 ) ) × 1 = 97.5
E: ( 97.5 ÷ 1 ) × 1 = 97.5

设置 flex-basis: auto

前面的例子里,我们都设置了 flex-basis 的值为 0,现在我们设置 flex-basis: auto,再来看看元素尺寸是如何计算的。

删除其他元素的 flex 指定,修改样式如下:

[id=c] {
flex: 5;
}

此时等价于其他元素是 flex: 0 1 auto,元素 C 是 flex: 5 1 0。展示效果如下:

在前面的例子中,所有元素都是 flex-basis: 0,现在设置为 A、B、D、E 都设置成了 flex-basis: auto。因此,元素 A、B、D、E 都会使用元素内容的尺寸来计算。

因此,剩余剩余可分配空间大小为:

1200 - ( 417 + 33 + 35 + 30 ) = 685

因为元素 C 是唯一可伸缩的元素,因此它的伸缩值为 685。

设置 flex-shrink

现在我们做一些调整,让所有 flex 元素的尺寸总和大于容器的可用空间尺寸,使得 flex-shrink 生效。

样式调整如下:

:not([id=a]) {
flex-shrink: 1;
}
[id=a] {
flex-shrink: 5;
}
div > div {
flex-basis: 500px;
}

可以看到,所有元素 flex-grow: 0,元素 A flex-shrink: 5,其他元素 flex-shrink: 1,所有元素总尺寸 2500px。

显示效果如下:

因为元素尺寸总和超过了容器可用尺寸(1200px),因此 flex-shrink 将会生效。

元素 A 的伸缩值为:

( 1300 ÷  ( 5 + 1 + 1 + 1 + 1 ) ) × 5 = 722.22

则元素 A 的实际尺寸计算为:

500 - 722.22 = -222.22

可以看到得出的是负值。如果元素 A 是空元素,那么最终尺寸会是零。本例中,元素 A 的尺寸就是起内容的尺寸,大概 34px。

确定了元素 A 的尺寸之后,容器剩余空间尺寸为 1200 - 34 = 1166。其他元素的伸缩值计算为:

B: ( 1166 ÷ ( 1 + 1 + 1 + 1 ) ) × 1 = 291.5
C: ( 874.5 ÷ ( 1 + 1 + 1 ) ) × 1 = 291.5
D: ( 583 ÷ ( 1 + 1 ) ) × 1 = 291.5
E: ( 291.5 ÷ 1 ) × 1 = 291.5

值得注意的一点是,如果 flex-growflex-shrink 的取值都为 0,那么元素即不会放大也不会缩小,当所有元素的尺寸总和超过容器空间之后,就会产生溢出效果。

设置 flex-wrap

当所有元素的尺寸总和超过容器空间之后,就会产生溢出效果。我们可以通过设置 flex-wrap: wrap 来是超出的元素换行。

我们调整下样式:

div > div {
flex-basis: 500px;
}
[id=flex] {
flex-wrap: wrap;
}

显示效果如下:

我们可以看到,由于 flex-basis: 500px 且默认的 flex-grow: 0,因此每一行的末尾有 200px 的剩余空间。

我们可以通过设置 flex-grow: 1 来让元素占满剩余空间。

div > div {
flex: 1 1 500px;
}

此时显示效果如下:

可以看到,每行的剩余空间都被占满了。

总结

Flexbox 布局有时候会有一些复杂和难以理解。在实际使用过程中,我们需要牢记如下几点:

  • Flexbox 只在单一方向上分配空间,行或者列。
  • flex-basis 定义了元素的最小尺寸,有时候元素的内容尺寸可能会比 flex-basis 定义的尺寸大。
  • 浏览器同时只会使用 flex-grow 或者 flex-shrink 来排列元素,不会同时使用。
  • 实际使用 flex-grow 还是 flex-shrink 取决于元素尺寸总和与容器剩余空间的大小。
  • 浏览器根据 flex 因子以及元素本身设置的系数来分配每个元素的空间。

关注微信公众号,获取最新推送~

加微信,深入交流~

原来 flexbox 是这么工作的的更多相关文章

  1. 使用顶级 VSCode 扩展来加快开发 JavaScript

    使用顶级 VSCode 扩展来加快开发 JavaScript 发表于 2018年08月24日 by 愚人码头 被浏览 3,942 次 分享到:   小编推荐:掘金是一个面向程序员的高质量技术社区,从 ...

  2. Flexbox 自由的布局

    css3提出了一种新的布局方式.她并没有以摧枯拉朽之势博得我的喜爱.我和她的故事总是伴随着苦涩的味道.世道变了,总要做出些选择才能跟紧步伐.她很强大,能满足你天马行空的需求而不必抓掉一大把头发.她却很 ...

  3. [译]flexbox全揭秘

    原文:http://css-tricks.com/snippets/css/a-guide-to-flexbox/ 弹性布局(弹性盒子,现今仍是w3c的候选推荐),目标在于,对于一个容器中的各个项目块 ...

  4. 前端工作面试问题--摘取自github

    前端工作面试问题 本文包含了一些用于考查候选者的前端面试问题.不建议对单个候选者问及每个问题 (那需要好几个小时).只要从列表里挑选一些,就能帮助你考查候选者是否具备所需要的技能. 备注: 这些问题中 ...

  5. flexbox-CSS3弹性盒模型flexbox完整版教程

    原文链接:http://caibaojian.com/flexbox-guide.html flexbox-CSS3弹性盒模型flexbox完整版教程 A-A+ 前端博客•2014-05-08•前端开 ...

  6. 转载:CSS3 Flexbox可视化指南

    0. 目录 目录 引言 正文 1 引入 2 基础 3 使用 4 弹性容器Flex container属性 41 flex-direction 42 flex-wrap 43 flex-flow 44 ...

  7. 【转】Flexbox——快速布局神器

    原文转自:http://www.w3cplus.com/css3/flexbox-basics.html 简介 在很多方面HTML和CSS是一个强大的内容发布机制——易学.灵活和强大.但复杂的布局是他 ...

  8. CSS3弹性盒模型flexbox完整版教程

    http://caibaojian.com/flexbox-guide.html 来自CSS Tricks上的一个教程,原文为:A Complete Guide to Flexbox.文中详细的介绍了 ...

  9. Flexbox——快速布局神器

    Flexbox通常能让我们更好的操作他的子元素布局,例如: 如果元素容器没有足够的空间,我们无需计算每个元素的宽度,就可以设置他们在同一行: 可以快速让他们布局在一列: 可以方便让他们对齐容器的左.右 ...

随机推荐

  1. Linux下忘记MySQL密码的解决办法

    一.使用免密码登录 1.使用 #find / -name my.cfg 找到mysql配置文件 2.vim /etc/mysql/my.cfg (我的配置文件是这个路径,每个人的路径可能有所不同,用f ...

  2. IDEA 连接MySQL并使用

    IDEA 连接MySQL方式 MySQL使用命令行的形式实在是有点难受,多行书写的时候要是写错了就比较难过了,因此还是需要一个图形化的操作界面.有很多可选择方式,由于之前就下载了IDEA,其中也继承了 ...

  3. python3中collections模块(转)

    https://www.cnblogs.com/zhangxinqi/p/7921941.html

  4. 二级py--day5 软件工程基础

    二级py--day5软件工程基础 软件工程基础 1.软件工程三要素:方法.工具和过程 2.软件生命周期可以分为:项目可行性研究与规划.软件需求分析.软件设计.软件实现.软件测试.软件运行与维护等阶段 ...

  5. Mybatis 多对一和一对多 学习总结04

    1.Mybatis 组件的声明周期 ​ 声明周期是组件的重要问题,Mybatis也常用语多线程环境,错误使用会造成多线程并发问题,为正确编写Mybatis应用程序,我们要掌握Mybatis组件的声明周 ...

  6. VS2022不能使用<bits/stdc++.h>的解决方案

    •<bits/stdc++.h>介绍  #include<bits/stdc++.h>  包含了目前 C++ 所包含的所有头文件,又称万能头文件,简直是开挂一般的存在. 你编程 ...

  7. [源码解析] TensorFlow 分布式环境(2)---Master 静态逻辑

    [源码解析] TensorFlow 分布式环境(2)---Master 静态逻辑 目录 [源码解析] TensorFlow 分布式环境(2)---Master 静态逻辑 1. 总述 2. 接口 2.1 ...

  8. ArcMap操作随记(9)

    1.类似PS中功能的工具 [镜像要素].[比例].[延伸] 2.快速获得栅格统计参数 [获取栅格属性]工具 3.[编辑器][创建要素][构造工具] 可以右键,输入半径等参数 4.计算面的角度 [计算面 ...

  9. 使用ntp 实现时间同步

    1.首先安装NTP [root@localhost /]# yum install ntp -y2.查看是否安装成功 rpm -qa|grep ntp [root@hadoop102 桌面]# vi ...

  10. linux下hadoop2.6.1源码64位的编译

    linux下hadoop2.6.1源码64位的编译 一. 前言 Apache官网上提供的hadoop本地库是32位的,如果我们的Linux服务器是64位的话,就会现问题.我们在64位服务器执行Hado ...