—— 分享前端技术,记录生活点滴

Flex简易教程

写在前面

  常见的前端布局模型涵盖浮动、定位和弹性盒等 CSS 技术,其中浮动和定位技术往往在制作自适应布局页面时显得不够优雅——对于浮动布局,前后端分离时代很多时候我们并不知道每行会遍历显示多少个元素,每个元素会被内容撑到多宽,所以每个元素的宽度百分比是很难去控制的;对于定位布局,在垂直居中(尤其是多个兄弟元素的垂直居中)样式上会显得比较吃力。鉴于但不限于以上两点,如今越来越多的开发者正选择弹性盒技术(即 Flex),如果你对这项技术还比较生疏,本文就其基本语法、兼容性写法以及典型实现等给出介绍,希望能够帮到你。

基本概念

  2009年,W3C 提出了一种新的布局方案——Flex 布局(Flexible Box 或 flexbox),它可以简单快速地创建一个响应式布局。如今,Flex 布局已得到所有主流浏览器的支持

Flex简易教程

  Flex 布局由Flex container(弹性容器)和Flex item(弹性子元素)组成。通过 CSS 设置display: flex即可将元素定义为弹性容器。弹性容器的所有子元素称为弹性子元素,弹性子元素默认在弹性容器内单行显示。不管是块级元素还是行内元素,任何一个元素的布局都可定义为 Flex 布局。元素设为 Flex 容器后,其子元素的浮动 float、清除浮动 clear 和垂直对齐 vertical-align 属性将失效。

1
2
3
4
5
6
7
8
9
/* flex 兼容性写法(注意书写顺序) */
.flex-container {
display: -webkit-flex; /* Chrome 21+, Safari 6.1+, iOS Safari 7+, Opera 15/16 */
display: -moz-flex; /* Firefox 18+ */
display: flex; /* Chrome 29+, Firefox 22+, IE 11+, Opera 12.1/17/18, Android 4.4+ */
display: -webkit-box; /* Chrome 4+, Safari 3.1, iOS Safari 3.2+ */
display: -moz-box; /* Firefox 17- */
display: -ms-flexbox; /* IE 10 */
}

基本语法

  首先简单了解下 Flex 布局的基本定义图。

Flex layout

  以下是所有 Flex 布局语法用到的 CSS 属性,总览如下:

弹性容器属性属性解释
flex-direction主轴方向,默认从左到右
flex-wrap换行显示,默认不换行
flex-flowflex-direction 和 flex-wrap 的简写
justify-content主轴对齐方式,默认左对齐
align-items交叉轴对齐方式,默认顶部对齐
align-content多主轴对齐方式,默认顶部对齐
弹性子元素属性属性解释
align-self自身特定对齐方式
flex-grow拉伸比率,默认不拉伸
flex-shrink收缩比率,默认不收缩
flex-basis占据主轴空间基准,默认为原始尺寸
flexflex-grow,flex-shrink 和 flex-basis 的简写
order排列顺序,数值越小越靠前

  接下来我们逐一来看。
  本文的基础布局见 codepen,为避免大篇幅重复内容,下文兼容写法中多处使用 sass @mixin 语法,关于语法详情,近期我将单独出教程,敬请期待。

弹性容器属性

>> flex-direction (主轴方向)
1
2
3
4
/* 常见 4 种取值 */
.flex-container {
flex-direction: row(默认) | row-reverse | column | column-reverse;
}

flex-direction

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
/* 兼容写法 */
@mixin flex-direction($value: row) {
-webkit-flex-direction: $value;
-ms-flex-direction: $value;
flex-direction: $value;
@if $value == row-reverse {
-webkit-box-direction: reverse;
-webkit-box-orient: horizontal;
-moz-box-direction: reverse;
-moz-box-orient: horizontal;
} @else if $value == column {
-webkit-box-direction: normal;
-webkit-box-orient: vertical;
-moz-box-direction: normal;
-moz-box-orient: vertical;
} @else if $value == column-reverse {
-webkit-box-direction: reverse;
-webkit-box-orient: vertical;
-moz-box-direction: reverse;
-moz-box-orient: vertical;
} @else {
-webkit-box-direction: normal;
-webkit-box-orient: horizontal;
-moz-box-direction: normal;
-moz-box-orient: horizontal;
}
}
>> flex-wrap (换行显示)
1
2
3
4
/* 常见 3 种取值 */
.flex-container {
flex-wrap: nowrap(默认) | wrap | wrap-reverse;
}

flex-wrap

1
2
3
4
5
6
7
8
9
10
11
/* 兼容写法 */
@mixin flex-wrap($value: nowrap) {
// No Webkit/FF Box fallback.
-webkit-flex-wrap: $value;
@if $value == nowrap {
-ms-flex-wrap: none;
} @else {
-ms-flex-wrap: $value;
}
flex-wrap: $value;
}
>> flex-flow (flex-direction 和 flex-wrap 的简写)
1
2
3
4
5
6
/* 默认兼容写法 */
@mixin flex-flow($values: (row nowrap)) {
-webkit-flex-flow: $values;
-ms-flex-flow: $values;
flex-flow: $values;
}
>> justify-content (主轴对齐方式)
1
2
3
4
/* 常见 5 种取值 */
.flex-container {
justify-content: flex-start(默认) | flex-end | center | space-between | space-around;
}

justify-content

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
/* 兼容写法 */
@mixin justify-content($value: flex-start) {
@if $value == flex-start {
-webkit-box-pack: start;
-moz-box-pack: start;
-ms-flex-pack: start;
} @else if $value == flex-end {
-webkit-box-pack: end;
-moz-box-pack: end;
-ms-flex-pack: end;
} @else if $value == space-between {
-webkit-box-pack: justify;
-moz-box-pack: justify;
-ms-flex-pack: justify;
} @else if $value == space-around {
-ms-flex-pack: distribute;
} @else {
-webkit-box-pack: $value;
-moz-box-pack: $value;
-ms-flex-pack: $value;
}
-webkit-justify-content: $value;
justify-content: $value;
}
>> align-items (交叉轴对齐方式)

  为展现效果,此处将本文基础布局稍作修改,点此查看

1
2
3
4
/* 常见 5 种取值 */
.flex-container {
align-items: stretch(默认)| flex-start | flex-end | center | baseline;
}

align-items

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
/* 兼容写法 */
@mixin align-items($value: stretch) {
@if $value == flex-start {
-webkit-box-align: start;
-moz-box-align: start;
-ms-flex-align: start;
} @else if $value == flex-end {
-webkit-box-align: end;
-moz-box-align: end;
-ms-flex-align: end;
} @else {
-webkit-box-align: $value;
-moz-box-align: $value;
-ms-flex-align: $value;
}
-webkit-align-items: $value;
align-items: $value;
}
>> align-content (多主轴对齐方式)
1
2
3
4
/* 常见 6 种取值,一般配合 flex-wrap: wrap 使用 */
.flex-container {
align-content: stretch(默认)| flex-start | flex-end | center | space-between | space-around;
}

align-content

1
2
3
4
5
6
7
8
9
10
11
12
/* 兼容写法 */
@mixin align-content($value: stretch) {
-webkit-align-content: $value;
@if $value == flex-start {
-ms-flex-line-pack: start;
} @else if $value == flex-end {
-ms-flex-line-pack: end;
} @else {
-ms-flex-line-pack: $value;
}
align-content: $value;
}

弹性子元素属性

>> align-self (自身特定对齐方式)

  align-self 属性允许单个弹性子元素有与其他弹性子元素不一样的对齐方式,权重大于 align-items 属性。其属性值表现与 align-items 属性的属性值表现一致。

1
2
3
4
/* 常见 6 种取值 */
.flex-container .flex-item {
align-self: auto(默认)| flex-start | flex-end | center | baseline | stretch;
}

  如现有弹性容器设置align-items: flex-start,给某个弹性子元素(如B)设置align-self: center,则表现为下图所示:

align-self

1
2
3
4
5
6
7
8
9
10
11
12
/* 兼容写法 */
@mixin align-self($value: auto) {
-webkit-align-self: $value;
@if $value == flex-start {
-ms-flex-item-align: start;
} @else if $value == flex-end {
-ms-flex-item-align: end;
} @else {
-ms-flex-item-align: $value;
}
align-self: $value;
}
>> flex-grow (拉伸比率)

  如果仅为某个或某几个(非全部)弹性子元素设置flex-grow: 1,那么这几个弹性子元素将被拉伸直至占满其所在的主轴行空间;如果存在弹性子元素设置flex-grow: 1,同时存在弹性子元素设置flex-grow: n(n大于1),则这些弹性子元素同样将被拉伸直至占满其所在的主轴行空间,并且n越大,所占的比例也就越大;如果所有弹性子元素均设置flex-grow: 1(相同的正整数),则它们将等分其所在的主轴行空间。

1
2
3
4
/* 常见 6 种取值 */
.flex-container .flex-item {
flex-grow: 0(默认)|(其他非负整数);
}

flex-grow

1
2
3
4
5
6
7
8
/* 兼容写法 */
@mixin flex-grow($int: 0) {
-webkit-flex-grow: $int;
-ms-flex: $int;
flex-grow: $int;
-webkit-box-flex: $int;
-moz-box-flex: $int;
}
>> flex-shrink (收缩比率)

  弹性子元素默认单行显示,如果空间不足,弹性子元素收缩自适应。flex-shrink 属性允许设置个别弹性子元素不被收缩,保持定宽。

1
2
3
4
/* 常见 6 种取值 */
.flex-container .flex-item {
flex-shrink: 1(默认)|(其他非负整数,常取0);
}

  如现有所有弹性子元素宽度均为 50px,由于主轴空间不足(未设置flex-wrap: wrap),所有弹性子元素收缩,宽度小于 50px,如果为某个弹性子元素(如B)设置flex-shrink: 0,则其保持 50px 不收缩,表现为下图所示:

flex-shrink

1
2
3
4
5
6
7
/* 兼容写法 */
@mixin flex-shrink($int: 1) {
-webkit-flex-shrink: $int;
-moz-flex-shrink: $int;
-ms-flex: $int;
flex-shrink: $int;
}
>> flex-basis (占据的主轴空间基准)

  flex-basis 属性可定义在分配主轴多余空间之前,弹性子元素所占据的主轴空间基准。

1
2
3
4
/* 常见取值 */
.flex-container .flex-item {
flex-basis: auto(默认) | <length>(可以是像素值,百分比等);
}
1
2
3
4
5
/* 兼容写法 */
@mixin flex-basis($value: auto) {
-webkit-flex-basis: $value;
flex-basis: $value;
}
>> flex (flex-grow, flex-shrink 和 flex-basis的简写)
1
2
3
4
/* 常见取值,建议优先选择前三种 */
.flex-container .flex-item {
flex: initial (0 1 auto)(默认) | auto (1 1 auto) | none (0 0 auto) | [ <'flex-grow'> <'flex-shrink'>? || <'flex-basis'> ];
}
1
2
3
4
5
6
7
8
9
/* 兼容写法 */
/* flex: 1 */
.flex-container .flex-item {
-webkit-flex: 1; /* Chrome */
-ms-flex: 1 /* IE 10 */
flex: 1; /* NEW, Spec - Opera 12.1, Firefox 20+ */
-webkit-box-flex: 1 /* OLD - iOS 6-, Safari 3.1-6 */
-moz-box-flex: 1; /* OLD - Firefox 19- */
}
>> order (排列顺序)
1
2
3
4
/* 常见取值 */
.flex-container .flex-item {
order: 0(默认)| (任何整数,数值越小,排列越靠前);
}

order

1
2
3
4
5
6
7
8
/* 兼容写法 */
@mixin order($int: 0) {
-webkit-order: $int;
-ms-flex-order: $int;
order: $int;
-webkit-box-ordinal-group: $int + 1;
-moz-box-ordinal-group: $int + 1;
}

典型实现

  典型实现从“圣杯布局”和“聊天界面”入手,来帮助读者进一步理解 Flex 布局。

圣杯布局

1
2
3
4
5
6
7
8
9
10
<!-- HTML -->
<section class="demo flex-wrapper">
<header class="header">头部</header>
<section class="flex-wrapper flex1">
<aside class="left-aside">left</aside>
<main class="flex1 bg-theme">center</main>
<aside class="right-aside">right</aside>
</section>
<footer class="footer">底部</footer>
</section>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
/* CSS */
* { padding: 0; margin: 0; }
html, body, .demo {
height: 100%;
}
.flex-wrapper {
display: flex;
}
.flex1 {
flex: 1;
}
.demo {
flex-direction: column;
}
.header, .footer {
flex: 0 0 50px;
}
.left-aside, .right-aside {
flex: 0 0 120px;
}
.bg-theme { background: #F2E966; }
.left-aside { background: #D9BB62; }
.right-aside { background: #F28749; }
.header { background: #32A685; }
.footer { background: #D95043; }

圣杯布局

  效果如上图。需要注意的点:

  • html、body 以及 .demo 设置height: 100%;保证页面撑满
  • header 和 footer 设置flex: 0 0 50px;保证页面 resize 不变形,保持 50px(aside 同理)

聊天界面

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
<!-- HTML -->
<section class="chatroom-wrapper">
<section class="msg-box">
<p class="msg-timestamp">2019-5-24 10:12:53</p>
<div class="msg-item self">
<div>
<img width="50" height="50" class="user-avatar" src="https://s2.ax1x.com/2019/05/24/Vieim6.png" alt="">
<p class='user-nick'>阿晋</p>
</div>
<div class="msg-bubble">哈喽</div>
</div>
<p class="msg-timestamp">2019-5-24 10:12:53</p>
<div class="msg-item">
<div>
<img width="50" height="50" class="user-avatar" src="https://s2.ax1x.com/2019/05/24/VieF0K.png" alt="">
<p class='user-nick'>阿美</p>
</div>
<div class="msg-bubble">嗨</div>
</div>
<p class="msg-timestamp">2019-5-24 10:12:53</p>
<div class="msg-item self">
<div>
<img width="50" height="50" class="user-avatar" src="https://s2.ax1x.com/2019/05/24/Vieim6.png" alt="">
<p class='user-nick'>阿晋</p>
</div>
<div class="msg-bubble">我给你背圆周率:3.1415926535897932384626433832795028841971693993751058209749445923078164...</div>
</div>
</section>
<footer class='footer'>
<input class="message-input" type="text" name="message" placeholder="说点什么..">
<input class="message-send" type="submit" value="发送"></input>
</footer>
</section>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
/* CSS */
* { padding: 0; margin: 0; }
html, body {
height: 100%;
}
.chatroom-wrapper {
height: 100%;
max-width: 640px;
margin: 0 auto;
display: flex;
flex-direction: column;
}
.msg-box {
flex: 1;
padding: 20px;
overflow-y: scroll;
background: skyblue;
}
.msg-timestamp {
font-size: 12px;
color: #666;
text-align: center;
margin-bottom: 10px;
}
.msg-item {
display: flex;
align-items: flex-start;
margin-bottom: 10px;
}
.msg-item.self {
flex-direction: row-reverse;
}
.user-avatar {
border-radius: 50%;
flex-shrink: 0;
cursor: pointer;
}
.user-nick {
font-size: 14px;
text-align: center;
}
.msg-bubble {
position: relative;
text-align: left;
margin: 0 15px;
padding: 15px;
font-size: 12px;
line-height: 1.5em;
border-radius: 10px;
word-break: break-all;
background: #fff;
}
.msg-bubble:after {
content: '';
position: absolute;
left: -12px;
top: 25px;
margin-top: -6px;
border: 6px solid skyblue;
border-right-color: #fff;
}
.msg-item.self .msg-bubble:after {
left: unset;
border-right-color: skyblue;
border-left-color: #fff;
right: -12px;
}
.footer {
display: flex;
flex-basis: 40px;
box-sizing: border-box;
border: 2px solid #ccc;
}
.footer .message-input {
flex: 1;
}
.footer .message-send {
flex-shrink: 0;
width: 50px;
}
.footer .message-input::-webkit-input-placeholder {
text-indent: 2em;
}
.footer input {
border: 0;
}

聊天界面

  效果如上图。需要注意的点:

  • 使用.self区分是自己还是对方的消息,使用flex-direction: row-reverse;控制消息条显示的左右位置
  • 消息条使用align-items: flex-start;保证消息内容太多时头像始终在最上沿的位置
  • 用户头像和发送按钮等为防止页面宽度压缩导致变形,给定宽度,使用flex-shrink: 0;撑开
  • 消息内容为连续数字或连续英文字母时,浏览器会误判为单个单词不去做换行处理,导致文本超出容器,设置word-break: break-all;解决
  • 消息框小箭头的左右定位使用left: unset;排除冲突

  以上所有便是我对 Flex 技术的一些整理,希望能对你有所帮助。

(END)

支付宝打赏 微信打赏

如果觉得我的文章对您有用,请随意赞赏 :)