最通俗易懂的flex讲解
目前在不考虑IE以及低端安卓机(4.3-)的兼容下,已经可以放心使用flex进行布局了。什么是flex布局以及它的好处,这里就不再赘述。
在这篇文章里,想说说flex布局的属性语法及其细节。那么网上也有不少flex布局的教程,为什么又要再写一篇?
首先,flex布局的迷之属性们,如果一知半解,机械记忆的话,那不到半个月基本忘光光。先感受一下这12个flex布局属性,是不是很“迷”人。
容器属性
- flex-flow
- flex-direction
- flex-wrap
- justify-content
- align-items
- align-content
元素属性
- order
- flex-grow
- flex-shrink
- flex-basis
- flex
- align-self
就连老外也都在twitter吐槽不好理解,可见还是有一定的学习成本。

而目前很多flex教程主要以列举属性为主,缺乏对比和理解性脉络。
因此,下面会通过我梳理的一个脉络去理解flex布局,包括不同属性的异同以及一些容易造成误解的细节点,彻底弄懂flex布局。
一、flex弹性盒模型
对于某个元素只要声明了display: flex;,那么这个元素就成为了弹性容器,具有flex弹性布局的特性。

- 每个弹性容器都有两根轴:主轴和交叉轴,两轴之间成90度关系。注意:水平的不一定就是主轴。
- 每根轴都有起点和终点,这对于元素的对齐非常重要。
- 弹性容器中的所有子元素称为<弹性元素>,弹性元素永远沿主轴排列。
- 弹性元素也可以通过
display:flex设置为另一个弹性容器,形成嵌套关系。因此一个元素既可以是弹性容器也可以是弹性元素。
弹性容器的两根轴非常重要,所有属性都是作用于轴的。下面从轴入手,将所有flex布局属性串起来理解。
二、主轴
flex布局是一种一维布局模型,一次只能处理一个维度(一行或者一列)上的元素布局,作为对比的是二维布局CSS Grid Layout,可以同时处理行和列上的布局。
也就是说,flex布局大部分的属性都是作用于主轴的,在交叉轴上很多时候只能被动地变化。
1. 主轴的方向
我们可以在弹性容器上通过flex-direction修改主轴的方向。如果主轴方向修改了,那么:
- 交叉轴就会相应地旋转90度。
- 弹性元素的排列方式也会发生改变,因为弹性元素永远沿主轴排列。
flex-direction:row
flex-direction:column
flex-direction:row-reverse
flex-direction:column-reverse
2. 沿主轴的排列处理
弹性元素永远沿主轴排列,那么如果主轴排不下,该如何处理?

通过设置flex-wrap: nowrap | wrap | wrap-reverse可使得主轴上的元素不折行、折行、反向折行。
默认是nowrap不折行,难道任由元素直接溢出容器吗?当然不会,那么这里就涉及到元素的弹性伸缩应对,下面会讲到。
wrap折行,顾名思义就是另起一行,那么折行之后行与行之间的间距(对齐)怎样调整?这里又涉及到交叉轴上的多行对齐。
wrap-reverse反向折行,是从容器底部开始的折行,但每行元素之间的排列仍保留正向。

3. 一个复合属性
flex-flow = flex-drection + flex-wrap
flex-flow相当于规定了flex布局的“工作流(flow)”
flex-flow: row nowrap;
三、元素如何弹性伸缩应对
当flex-wrap: nowrap;不折行时,容器宽度有剩余/不够分,弹性元素们该怎么“弹性”地伸缩应对?
这里针对上面两种场景,引入两个属性(需应用在弹性元素上)
flex-shrink:缩小比例(容器宽度<元素总宽度时如何收缩)flex-grow:放大比例(容器宽度>元素总宽度时如何伸展)
1. flex-shrink: 缩小比例
来看下以下场景,弹性容器#container宽度是200px,一共有三个弹性元素,宽度分别是50px、100px、120px。在不折行的情况下,此时容器宽度是明显不够分配的。
实际上,flex-shrink默认为1,也就是当不够分配时,元素都将等比例缩小,占满整个宽度,如下图。

#container {
display: flex;
flex-wrap: nowrap;
}
元素收缩的计算方法
真的是等比缩小(每个元素各减去70/3的宽度)吗?这里稍微深究一下它的收缩计算方法。
- 弹性元素1:50px→37.03px
- 弹性元素2:100px→74.08px
- 弹性元素3:120px→88.89px
先抛结论:flex-shrink: 1并非严格等比缩小,它还会考虑弹性元素本身的大小。
- 容器剩余宽度:
-70px - 缩小因子的分母:
1*50 + 1*100 + 1*120 = 270(1为各元素flex-shrink的值) - 元素1的缩小因子:
1*50/270 - 元素1的缩小宽度为缩小因子乘于容器剩余宽度:
1*50/270 * (-70) - 元素1最后则缩小为:
50px + (1*50/270 *(-70)) = 37.03px
加入弹性元素本身大小作为计算方法的考虑因素,主要是为了避免将一些本身宽度较小的元素在收缩之后宽度变为0的情况出现。
2. flex-grow: 放大比例
同样,弹性容器#container宽度是200px,但此时只有两个弹性元素,宽度分别是50px、100px。此时容器宽度是有剩余的。
那么剩余的宽度该怎样分配?而flex-grow则决定了要不要分配以及各个分配多少。
(1)在flex布局中,容器剩余宽度默认是不进行分配的,也就是所有弹性元素的flex-grow都为0。

(2)通过指定flex-grow为大于零的值,实现容器剩余宽度的分配比例设置。

元素放大的计算方法
放大的计算方法并没有与缩小一样,将元素大小纳入考虑。
仅仅按flex-grow声明的份数算出每个需分配多少,叠加到原来的尺寸上。
- 容器剩余宽度:
50px - 分成每份:
50px / (3+2) = 10px - 元素1放大为:
50px + 3 * 10 = 80px
无多余宽度时,flex-grow无效
下图中,弹性容器的宽度正好等于元素宽度总和,无多余宽度,此时无论flex-grow是什么值都不会生效。

同理,对于flex-shrink,在容器宽度有剩余时也是不会生效的。因此这两个属性是针对两种不同场景的互斥属性。
四、弹性处理与刚性尺寸
在进行弹性处理之余,其实有些场景我们更希望元素尺寸固定,不需要进行弹性调整。设置元素尺寸除了width和height以外,flex还提供了一个flex-basis属性。
flex-basis设置的是元素在主轴上的初始尺寸,所谓的初始尺寸就是元素在flex-grow和flex-shrink生效前的尺寸。
1. 与width/height的区别
首先以width为例进行比较。看下下面的例子。#container {display:flex;}。
<div id="container">
<div>11111</div>
<div>22222</div>
</div>
(1) 两者都为0

- width: 0 —— 完全没显示
- flex-basis: 0 —— 根据内容撑开宽度
(2) 两者非0

- width: 非0;
- flex-basis: 非0
—— 数值相同时两者等效
—— 同时设置,flex-basis优先级高
(3) flex-basis为auto

flex-basis为auto时,如设置了width则元素尺寸由width决定;没有设置则由内容决定
(4) flex-basis == 主轴上的尺寸 != width

- 将主轴方向改为:上→下
- 此时主轴上的尺寸是元素的height
- flex-basis == height
2. 常用的复合属性 flex
这个属性应该是最容易迷糊的一个,下面揭开它的真面目。
flex = flex-grow + flex-shrink + flex-basis
复合属性,前面说的三个属性的简写。

一些简写
flex: 1=flex: 1 1 0%flex: 2=flex: 2 1 0%flex: auto=flex: 1 1 auto;flex: none=flex: 0 0 auto;// 常用于固定尺寸 不伸缩
flex:1 和 flex:auto 的区别
其实可以归结于flex-basis:0和flex-basis:auto的区别。
flex-basis是指定初始尺寸,当设置为0时(绝对弹性元素),此时相当于告诉flex-grow和flex-shrink在伸缩的时候不需要考虑我的尺寸;相反当设置为auto时(相对弹性元素),此时则需要在伸缩时将元素尺寸纳入考虑。
因此从下图(转自W3C)可以看到绝对弹性元素如果flex-grow值是一样的话,那么他们的尺寸一定是一样的。

五、容器内如何对齐
前面讲完了元素大小关系之后,下面是另外一个重要议题——如何对齐。可以发现上面的所有属性都是围绕主轴进行设置的,但在对齐方面则不得不加入作用于交叉轴上。需要注意的是这些对齐属性都是作用于容器上。
1. 主轴上的对齐方式
justify-content

2. 交叉轴上的对齐方式
主轴上比较好理解,重点是交叉轴上。因为交叉轴上存在单行和多行两种情况。
交叉轴上的单行对齐
align-items
默认值是stretch,当元素没有设置具体尺寸时会将容器在交叉轴方向撑满。
当align-items不为stretch时,此时除了对齐方式会改变之外,元素在交叉轴方向上的尺寸将由内容或自身尺寸(宽高)决定。





注意,交叉轴不一定是从上往下,这点再次强调也不为过。

交叉轴上的多行对齐
还记得可以通过flex-wrap: wrap使得元素在一行放不下时进行换行。在这种场景下就会在交叉轴上出现多行,多行情况下,flex布局提供了align-content属性设置对齐。
align-content与align-items比较类似,同时也比较容易迷糊。下面会将两者对比着来看它们的异同。
首先明确一点:align-content只对多行元素有效,会以多行作为整体进行对齐,容器必须开启换行。
align-content: stretch | flex-start | flex-end | center | space-between | space-around
align-items: stretch | flex-start | flex-end | center | baseline
在属性值上,align-content比align-items多了两个值:space-between和space-around。
align-content与align-items异同对比
与align-items一样,align-content:默认值也是stretch。两者同时都为stretch时,毫无悬念所有元素都是撑满交叉轴。
#container {
align-items: stretch;
align-content: stretch;
}

当我们将align-items改为flex-start或者给弹性元素设置一个具体高度,此时效果是行与行之间形成了间距。
#container {
align-items: flex-start;
align-content: stretch;
}
/或者/
#container {
align-content: stretch;
}
#container > div {
height: 30px;
}
为什么?因为align-content会以整行为单位,此时会将整行进行拉伸占满交叉轴;而align-items设置了高度或者顶对齐,在不能用高度进行拉伸的情况下,选择了用间距。

尝试把align-content设置为顶对齐,此时以行为单位,整体高度通过内容撑开。
而align-items仅仅管一行,因此在只有第一个元素设置了高度的情况下,第一行的其他元素遵循align-items: stretch也被拉伸到了50px。而第二行则保持高度不变。
#container {
align-items: stretch;
align-content: flex-start;
}
#container > div:first-child {
height: 50px;
}

两者的区别还是不明显?来看下面这个例子。
这里仅对第二个元素的高度进行设置,其他元素高度则仍保持内容撑开。

以第一个图为例,会发现align-content会将所有行进行顶对齐,然后第一行由于第二个元素设置了较高的高度,因此体现出了底对齐。
两者差异总结:
- 两者“作用域”不同
- align-content管全局(所有行视为整体)
- align-items管单行
能否更灵活地设置交叉轴对齐
除了在容器上设置交叉轴对齐,还可以通过align-self单独对某个元素设置交叉轴对齐方式。
- 值与
align-items相同 - 可覆盖容器的
align-items属性 - 默认值为
auto,表示继承父元素的align-items属性

#container {
display: flex;
align-items: flex-start;
}
#container > div:first-child {
align-self: stretch;
}
#container > div:nth-child(3) {
align-self: center;
}
#container > div:nth-child(4) {
align-self: flex-end;
}
六、其他
order:更优雅地调整元素顺序

#container > div:first-child {
order: 2;
}
#container > div:nth-child(2) {
order: 4;
}
#container > div:nth-child(3) {
order: 1;
}
#container > div:nth-child(4) {
order: 3;
}
order:可设置元素之间的排列顺序
- 数值越小,越靠前,默认为0
- 值相同时,以dom中元素排列为准
七、总结



附
参考阮老师博文中的骰子练习,我做了张图,大家不妨可以各自实现下,理解之后应该能够比较轻松地写出来。codepen

参考资料
深入理解css3中的flex-grow、flex-shrink、flex-basis
相关阅读
bitset用法小结
Numpy基础知识点汇总
【每日课程推荐】机器学习实战!快速入门在线广告业务及CTR相应知识
此文转至https://www.cnblogs.com/qcloud1001/p/9848619.html点击阅读原文
最通俗易懂的flex讲解的更多相关文章
- 通俗易懂--SVM算法讲解(算法+案例)
1.SVM讲解 新闻分类案例 SVM是一个很复杂的算法,不是一篇博文就能够讲完的,所以此篇的定位是初学者能够接受的程度,并且讲的都是SVM的一种思想,通过此篇能够使读着会使用SVM就行,具体SVM的推 ...
- 用通俗易懂的大白话讲解Map/Reduce原理
Hadoop简介 Hadoop就是一个实现了Google云计算系统的开源系统,包括并行计算模型Map/Reduce,分布式文件系统HDFS,以及分布式数据库Hbase,同时Hadoop的相关项目也很丰 ...
- 通俗易懂的ListView讲解(Adapter、图、实例)
2016/4/5 17:22] 之前写listview其实写了很多次,但好像还是模模糊糊的感觉,直到今天准备写tab的时候被告诉说原理有像的地方,于是我就先来分析整理一下listview好了 先来 ...
- 深入新版BS4源码 探索flex和工程化sass奥秘
你可能已经听说了一个“大新闻”:Bootstrap4 合并了代号为#21389的PR,宣布放弃支持IE9,并默认使用flexbox弹性盒模型.这标志着:1)前端开发全面步入“现代浏览器”的时代进一步来 ...
- 【ACM数论】和式变换技术,也许是最好的讲解之一
在做数论题时,往往需要进行和式变换,然后变换成我们可以处理的和式,再针对和式做筛法.整除分块等操作. 本文将介绍一些常见的和式变换技术. 以下出现的概念大部分为个人总结,未必是学术界/竞赛界的统一说法 ...
- 一个「学渣」从零开始的Web前端自学之路
从 13 年专科毕业开始,一路跌跌撞撞走了很多弯路,做过餐厅服务员,进过工厂干过流水线,做过客服,干过电话销售可以说经历相当的“丰富”. 最后的机缘巧合下,走上了前端开发之路,作为一个非计算机专业且低 ...
- 月薪12k的零基础自学前端必备手册
随着互联网的深入发展,前端开发工程师一跃成为市场上非常抢手的人才.很多同学,包括以前做UI的.Java的.或者对于IT完全零基础的同学都想学习前端.下图是网上流传甚广的一张前端学习思维导图,很多初学者 ...
- 从零开始编写自己的C#框架(28)——建模、架构与框架
文章写到这里,我一直在犹豫是继续写针对中小型框架的设计还是写些框架设计上的进阶方面的内容?对于中小型系统来说,只要将前面的内容进行一下细化,写上二三十章具体开发上的细节,来说明这个通用框架怎么开发的就 ...
- Kinect for Windows SDK开发学习相关资源
Kinect for Windows SDK(K4W)将Kinect的体感操作带到了平常的应用学习中,提供了一种不同于传统的鼠标,键盘及触摸的无接触的交互方式,在某种程度上实现了自然交互界面的理想,即 ...
- 使用git推送代码到开源中国以及IDEA环境下使用git
使用git推送代码到开源中国以及IDEA环境下使用git 在学习Java的过程中我们会使用到git这个工具来将我们本周所编写的代码上传到开源中国进行代码托管,而在使用git的时候有很多的同学由于不会操 ...
随机推荐
- 把 ChatGPT 加入 Flutter 开发,会有怎样的体验?
前言 ChatGPT 最近一直都处于技术圈的讨论焦点.它除了可作为普通用户的日常 AI 助手,还可以帮助开发者加速开发进度.声网社区的一位开发者"小猿"就基于 ChatGPT 做了 ...
- (数据科学学习手札150)基于dask对geopandas进行并行加速
本文示例代码已上传至我的Github仓库https://github.com/CNFeffery/DataScienceStudyNotes 1 简介 大家好我是费老师,geopandas作为我们非常 ...
- java多线程--3 线程状态、线程方法、线程类型
java多线程--3 线程状态.线程方法.线程类型 线程状态 创建状态: **就绪状态: ** 进入状态: 创建状态:启动线程 阻塞状态:阻塞解除 运行状态:释放CPU资源 阻塞状态: 进入状态: 运 ...
- JVM Dump分析
Thread Dump介绍 Thread Dump是非常有用的诊断 Java应用问题的工具.每一个 Java虚拟机都有及时生成所有线程在某一点状态的 thread-dump的能力,虽然各个 Java虚 ...
- AlphaFold2中的残基刚体表示
技术背景 在前面的这一篇博客中,比较全面的介绍了组成蛋白质的各种氨基酸的三维结构.由于每个氨基酸大小不一,在传统的蛋白质折叠预测的方案中,一般会考虑全原子方案或者是粗粒化方案.对于全原子方案而言,即时 ...
- Unity实现3D物体遮挡血条
Unity 实现3D物体遮挡血条 前言:在游戏开发中,我们经常会遇到UI和3D物体的层级遮挡问题,最常见的比如血条跟随敌人的时候,多个敌人的血条会遮挡住玩家或者3D物体,去网上查了一下也没有很好的解决 ...
- ByteHouse MaterializedMySQL 增强优化
更多技术交流.求职机会,欢迎关注字节跳动数据平台微信公众号,回复[1]进入官方交流群 前言 社区版 ClickHouse 推出了MaterializedMySQL数据库引擎,用于将 MySQL 中的表 ...
- 内核不中断前提下,Gaussdb(DWS)内存报错排查方法
摘要:本文主要讲解如何在内核保证操作不能中断采取的特殊处理,理论上用户执行的sql使用的内存(dynamic_used_memory) 是不会大范围的超过max_dynamic_memory的内存的 ...
- 2023年php面试题
Php面试题 1.isset和empty的区别? Isset测试变量是否被赋值,如果这个变量没被赋值,则返回false,empty是判断变量是否为空,当赋值为0,null,'',返回true为真.他们 ...
- 月光宝盒(vivo流量录制回放平台)正式对外开源
作者:vivo 互联网服务器团队- Liu Yanjiang 月光宝盒是一个基于流量录制回放的自动化测试平台,通过录制回放取代编写脚本进行自动化回归,提升测试效率和覆盖率.因为其解决方案具有很强的通用 ...