前言
很长一段时间, 我知道有flex这个布局方式, 但是始终没有去学它. 3点原因:
- 感觉还比较新, 担心兼容性不好.
- 普通的布局方式能满足我的绝大多数需求.
- 好像蛮复杂的.
最近由于开发需要, 学习了下WeUI的实现, 发现里面大量使用了flex布局, 于是决定学习一下.
什么是flex
Flexbox Layout, 官方名为CSS Flexible Box Layout Module, 意为"弹性布局", 是CSS3中引入的一种更加灵活高效的布局/对齐/排序方式(还有一种更适合大型布局的网格布局CSS Grid Layout Module). flex是flexible的缩写.
任何一个容器都可以指定为flex布局。
.box {
display: flex;
}
行内元素也可以使用flex布局。
.box {
display: inline-flex;
}
flex的基本概念
采用flex布局的元素被称为flex容器 (flex container), 它的子元素即为flex元素 (flex item).
flex容器中包含两个相互垂直的轴, 即主轴 (main axis)和副轴 (cross axis).
flex元素沿主轴从**主轴起点 (main start)到主轴终点 (main end)**依次排布.
如果flex容器包含多行flex元素, 则**flex行 (flex lines)沿副轴从副轴起点 (cross start)到副轴终点 (cross end)**依次排布.
单个flex元素占据的主轴空间叫做主轴长度 (main size), 占据的副轴空间叫做副轴长度 (cross size).
flex的兼容性
Getting Dicey With Flexbox中提到:
There's a popular myth floating around that flexbox isn't ready for prime time. Wrong! 93% of people are now running a browser that supports flexbox. That's better than the support for the HTML5
<video>element.
前一段时间同事做过video相关的开发, 踩到各种坑, 因此我知道video的支持不那么好, 特别是在Android上. 让我惊奇的是flex竟然比video的支持更好?
从CanIUse的数据来看, flex的支持度是: 82.65% (支持) + 14.17% (部分支持) = 96.81%, 而video的支持度是: 92.48%. 浏览器对flex的支持好像并没有特别好...
但是有微信的WeUI使用了flex布局, 我觉得在移动端flex应该还是支持度比较高的.
所以, 如果你是做移动端开发的, 可以优先考虑flex.
flex属性
下面就开始介绍与flex布局相关的属性. 以作用对象分为两组, 第一组作用于flex容器, 第二组作用于flex元素.
注意: 以下属性值都可以有initial(该属性的默认值)和inherit(继承自父元素), 本处省略.
用于flex容器的属性
这类属性有6种, 分别为:
| 属性 | 含义 |
|---|---|
flex-direction | 主轴方向 |
flex-wrap | 换行样式 |
flex-flow | 前两个的简写形式 |
justify-content | 主轴对齐方式 |
align-items | 单行的副轴对齐方式 |
align-content | 多行的副轴对齐方式 |
注意:
- flex容器的
column-*属性会失效. - flex容器无法拥有
::first-line和::first-letter虚元素.
flex-direction
| 含义 | 主轴方向 | |||
| 可选值 | `row | row-reverse | column | column-reverse` |
| 默认值 | row | |||
row | direction为ltr时从左向右→, rtl时从右向左←. | |||
row-reverse | direction为ltr时从右向左←, rtl时从左向右→. | |||
column | 从上到下↓. | |||
column-reverse | 从下到上↑. |
注意: row和row-reverse受到了direction属性(默认值为ltr, 可改为rtl)的影响.
flex-wrap
| 含义 | 换行样式 | ||
| 可选值 | `nowrap | wrap | wrap-reverse` |
| 默认值 | nowrap | ||
nowrap | 不换行 | ||
wrap | 换行. 行与行从上到下↓排布 | ||
wrap-reverse | 换行. 行与行从下到上↑排布 |
flex-flow
| 含义 | flex-direction和flex-wrap的简写形式 |
| 可选值 | flex-direction flex-wrap |
| 默认值 | row nowrap |
justify-content
| 含义 | 主轴对齐方式 | ||||
| 可选值 | `flex-start | flex-end | center | space-between | space-around` |
| 默认值 | flex-start |
align-items
| 含义 | 单行的副轴对齐方式 | ||||
| 可选值 | `flex-start | flex-end | center | stretch | baseline` |
| 默认值 | stretch |
align-content
| 含义 | 多行的副轴对齐方式 | |||||
| 可选值 | `stretch | flex-start | center | flex-end | space-between | space-around` |
| 默认值 | stretch |
注意: 此属性只在flex容器中有多行flex元素时才有作用.
用于flex元素的属性
这类属性有6种, 分别为:
| 属性 | 含义 |
order | 排列顺序 |
align-self | flex元素的副轴对齐方式. 对应于flex容器的align-items. |
flex-grow | 放大比例 |
flex-shrink | 缩小比例 |
flex-basis | 初始大小 |
flex | 上面三个的简写形式 |
注意: flex元素的float, clear和vertical-align会失效.
order
| 含义 | 排列顺序. 沿着主轴, flex元素按order的增序排列. |
| 可选值 | <integer> |
| 默认值 | 0 |
align-self
| 含义 | flex元素的副轴对齐方式. 对应于flex容器的align-items. | |||||
| 可选值 | `auto | stretch | center | flex-start | flex-end | baseline` |
| 默认值 | auto |
当flex元素有父元素时, 它的align-self: auto即为父元素的align-items属性; 否则(无父元素时), 相当于stretch.
flex-grow
| 含义 | 放大比例 |
| 可选值 | <number> (非负值) |
| 默认值 | 0 |
当有剩余空间时, flex元素会根据flex-grow按比例分配剩余空间.
默认值0代表, 即使有剩余空间, 该flex元素也不放大.
flex-shrink
| 含义 | 缩小比例 |
| 可选值 | <number> (非负值) |
| 默认值 | 1 |
当flex容器空间不足时, flex元素会根据flex-shrink按比例缩小.
flex-shrink为0则表示, 即使flex容器空间不足, 该flex元素也不缩小.
flex-basis
| 含义 | 初始大小 | |
| 可选值 | `auto | ` (非负值) |
| 默认值 | auto |
flex-basis定义了分配剩余空间之前flex元素的初始大小, 可为长度值(如20%, 5rem等)或auto等关键词.
flex-basis: auto表示, 以flex元素的主轴长度为flex-basis. 若flex元素的主轴长度也是auto, 则以flex元素内容(即所有子元素)的大小为flex-basis.
除了auto还有content, max-content, min-content和fit-content关键词, 但是现在浏览器对它们的支持太少, 可以忽略.
flex
| 含义 | flex-grow, flex-shrink和flex-basis的简写形式 | |||
| 可选值 | `none | [ <‘flex-grow’> <‘flex-shrink’>? | <‘flex-basis’> ]` | |
| 默认值 | 0 1 auto |
(敲黑板) 同学们注意, 这里是重点!
这里的可选值我参照了W3C flexbox的写法. 其中:
||用来分割两个或多个选项, 从中选取一个或多个, 不限次序.|用来分割两个或多个选项, 从中选取一个.[]只是用来分组的.?代表可选.
举例来说, a | [ b || c ]包含的可能情况有a, b, c, b c, c b.
现在回过头来再看none | [ <‘flex-grow’> <‘flex-shrink’>? || <‘flex-basis’> ]就清晰多了.
注意, none是一个特殊值, 相当于0 0 auto.
另外, 如果flex中不指定:
flex-grow成员, 则flex-grow会被置为1.flex-shrink成员, 则flex-shrink会被置为1.flex-basis成员, 则flex-basis会被置为0.
注意: flex的初始值是0 1 auto, 即由每个flex因子本身的默认值组成(比方说flex-grow的默认值就是0).
但是, 如果利用flex设置了至少一个flex因子, 那么没被设置的那些flex因子的默认值(按grow, shrink, basis的顺序)分别是1 1 0.
我来举几个栗子.
/* 特殊值none */
flex: none; /* 相当于0 0 auto */
/* 单值,没有单位的数字,是flex-grow */
flex: 2; /* 相当于 flex: 2 1 0 */
/* 单值,有单位的,宽、高,是flex-basis */
flex: 10em; /* 相当于 flex: 1 1 10em */
flex: 30px; /* 相当于 flex: 1 1 30px */
flex: auto; /* 相当于 flex: 1 1 auto */
flex: content; /* 相当于 flex: 1 1 content */
/* 两个值:flex-grow flex-basis */
flex: 1 30px; /* 相当于 flex: 1 1 30px */
/* 两个值:flex-grow flex-shrink */
flex: 2 2; /* 相当于 flex: 2 2 0 */
/* 三个值:flex-grow flex-shrink flex-basis */
flex: 2 2 10%;
W3C建议使用简写形式flex, 因为它可以方便地应对下面4种常见情况.
flex: initial | 即flex: 0 1 auto. 以auto方式计算flex-basis, 不可放大, 可缩小. |
flex: auto | 即flex: 1 1 auto. 以auto方式计算flex-basis, 可放大, 可缩小. |
flex: none | 即flex: 0 0 auto. 以auto方式计算flex-basis, 不可放大, 不可缩小. |
flex: <positive-number> | 即<positive-number> 1 0. flex-basis为0, 以<positive-number>比例增大, 以1的比例缩小. |
flex元素大小的计算方法
自此, 我们已经知道了flex-grow, flex-shrink和flex-basis的作用. 根据这三个值, 计算flex元素的大小只需三步:
第一步: 计算元素的flex-basis, 有两种情况: 1. 具体的长度值, 或, 2.auto(即flex元素的大小). (这里忽略了content等目前支持还不完善的关键词).
第二步: 计算剩余空间, 即剩余空间 = flex容器的内部空间 - flex元素flex-basis值的总和.
第三步: 按照flex因子(放大时为flex-grow; 缩小时为flex-shrink)分配剩余空间到每个元素. flex元素的最终大小 = flex-basis - flex-factor * 剩余空间.
举个栗子.
假设flex容器的内部空间为200px, flex元素的大小的总和是160px. 看起来, 还有200 - 160 = 40px的剩余空间, 应该放大flex元素, 是不是? 不一定! 要看它们的flex-basis总和.
假设它们的flex-basis总和是300px, 那么剩余空间应该是300 - 200 = -100px. 此时剩余空间是负数, 应该以flex-shrink对每个flex元素在flex-basis的基础上进行缩小.
下例中, 所有flex元素本身的大小为80px, 元素中为flex值.
200px
0 1 auto
0 3 auto
0 1 150px
0 3 150px
125px
75px
你可以看到, 第一行的flex元素因为设置了flex-basis:auto, 所以它们的flex-basis就相当于元素大小, 即80px, 即flex-basis总和为160px, 不足容器的200px空间, 此时应该放大元素. 但又由于元素的flex-grow为0, 所以每个元素分配到0 * 40 = 0px的剩余空间, 即不放大.
第二行的flex元素设置了flex-basis:150px, 所以它们的flex-basis总和为300px, 超过了容器的200px空间, 故按照flex-shrink(比例为1:3)进行缩小. 由于剩余空间为-100px, 所以第一个元素应缩小25px变成125px, 第二个元素应缩小75px变成75px.
"绝对flex"和"相对flex"
绝对flex: 从0开始分配空间.
第一行中flex-basis为0, 表示每个flex元素的初始大小都视为0. 此时, 剩余空间就是"flex容器的大小".
相对flex: 从flex元素大小开始分配空间.
第二行中flex-basis为auto, 表示每个flex元素的初始大小都是它本身的大小. 此时, 剩余空间就是"flex容器的大小 - flex元素大小的总和".
结语
呃... flex的东西还是挺多的, 特别是flex因子相关的部分, 得花点儿时间理解.
但是, 我相信学flex是值得的, 谁用谁知道!