在
CSS2
的时代,前端的布局基本上采用标准流配合浮动来进行开发,从CSS3
开始提供了Flex
布局(弹性布局)来适应移动端的发展潮流,Flex
布局更加灵活,能为不同尺寸的设备提供适配的布局,在iOS
中其实有一个stackView
的布局方式和Flex
布局非常相似。
初步理解Flex
Flex
布局在整个页面中的布局主要和float
布局的地位差不多,主要是确定页面中主要模块的布局,至于模块内部的布局则依然可以采用position
的方式来布局。要学习Flex
布局,一开始会有很多概念需要理解,其实这些概念都是确认一个flex-item
的位置所需要的,下面先抛砖引玉进行一些通俗的说明。其实Flex
布局的过程可以简单的理解为在黑板上写字的过程,先抛开固定的写字思维:即在黑板上开始写字时不一定非要从黑板的左上角开始写,然后依次从左向右,写完一行后在移到下一行,再次从左向右,直至反复将黑板写满。为啥不能一开始从黑板的右上角,左下角,右下角开始书写?书写的顺序从右向左?写完一行,下一行在写完这行的上面?每一行文字的是左对齐,右对齐或者二端对齐?行与行之间是多大的间距?上面的规律组合可以形成很多种不同的书写格局,Flex
布局的原理和这差不多,也是通过不同的参数设定,来保证在不同的黑板大小下形成不同的布局方式,即使不同的浏览器尺寸,不同的移动端屏幕大小显示不同的布局。
Flex基本的理解
开局一张图,那么我们就双手奉上Flex
解析最经典的一张图:
-
main axis
:称为主轴,flex items
主要的布局方向,不一定是水平的(就像对联是从上往下写一样,当然你乐意也是可以倒着从下往上写),其方向由属性flex-direction
决定。 -
cross-axis
:称为交叉轴,相对于主轴而言,与主轴垂直,其方向要基于main-axis
而定。 -
main-start | main-end
:flex items
在main-axis
是从main-start
到main-end
排布的。 -
cross-start | cross-end
: 一条line
上的flex items
在cross axis
上是从cross-start
到cross-end
排布的,就像对联是先读右联在读左联一样,但有些对联你先读左联在读右联好像也通顺。 -
main size | cross size
:主轴和交叉轴的大小。
Flex属性
display
黑板在Flex
中称为flex container
,写的字则称为flex items
是flex container
的直接子元素,display
属性是设置在flex container
上的,有二个取值:
- flex
- inline-flex
.container {
display: flex; /* or inline-flex */
}
如果
flex container
的display
设置为flex
的话,那么flex container
是以block-level
的形式存在的,相当于是一个块级元素,如果display
的值设置为inline-flex
的话,那么flex container
是以inline-level
的形式存在的,相当于是一个行内块元素;父元素设置了display: flex
,即使子元素设置了display:block
或者display:inline
的属性,子元素还是会表现的像个行内块元素一样,但是假如flex container
和flex items
都设置了display: flex
,那么flex items
自身依然是行块级元素,并不会因为其开启了flex
布局就变为块级元素,但是flex items
的内容依然会受到它flex
布局的影响,各种flex
特有的属性就会生效。
flex-direction
这个属性确定了主轴以及主轴的方向,实质是改变了main-start
和main-end
的位置来改变flex items
在main axis
上的排布方向,一共有四个取值如下:
-
row (default)
:水平方向从左向右。 -
row-reverse
:水平方向从右向左。 -
column
:垂直方向从上到下。 -
column-reverse
:垂直方向从下到上。
.container {
flex-direction: row | row-reverse | column | column-reverse;
}
flex-wrap
flex-wrap
决定了是否需要换行来显示,默认情况下flex items
是尽量一行显示的(会自动缩放flex item
的宽度),可以通过设置此属性要决定是否多行显示:
-
nowrap (default)
:一行显示。 -
wrap
:多行显示,根据交叉轴的方向来决定,主轴是横向则从上到下,主轴是纵轴则从左到右。 -
wrap-reverse
:多行显示,将cross-start
和cross-end
的位置互换,主轴是横向则从下到上,主轴是纵向则从右到左。
- 主轴只要是横向的,无论
flex-direction
设置的是row
还是row-reverse
,其交叉轴都是从上指向下的,所以当flex-wrap
设置为wrap
属性时,cross-start
在上面而cross-end
在下面;但设置为wrap-reverse
时则cross-start
在下面,cross-end
在上面;- 主轴只要是纵向的,无论
flex-direction
设置的是column
还是column-reverse
,其交叉轴都是从左指向右的,所以当flex-wrap
设置为wrap
属性时,cross-start
在左边而cross-end
在右边;但设置为wrap-reverse
时则cross-start
在右边,cross-end
在左边;
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8">
<title>Example of Perfect Center Alignment Using Flexbox</title>
<style>
.flex-container {
width: 500px;
min-height: 600px;
margin: 0 auto;
font-size: 22px;
border: 1px solid #666;
display: -webkit-flex; /* Safari 6.1+ */
display: flex; /* Standard syntax */
flex-wrap: wrap-reverse;
}
.item {
width: 200px;
height: 200px;
margin-left: 20px;
line-height: 200px;
text-align: center;
background: #f0e68c;
}
</style>
</head>
<body>
<div class="flex-container">
<div class="item">1</div>
<div class="item">2</div>
<div class="item">3</div>
</div>
</body>
</html>
结果如下:
在纵向的排布方向永远是
cross-start
--->cross-end
的(横向的原理也是一样),而起始点的位置则由align-content
属性决定,至于这里的3号方块为什么不是紧挨着1号的上边缘,这个也和align-content
属性有关,后面会作解释。
flex-flow
这个属性就是上面flex-direction
和flex-wrap
的缩写,可以只写一个属性也可以二个都写,且与顺序无关。
.container {
flex-flow: column wrap;
}
justify-content
在黑板上写字默认的习惯是每个字紧挨着,但是有人却每次写字都能保证一行只写10个字,无论黑板多宽我就写10个字,且每个字之间的距离相等,这时你脑海肯定想着拿尺子量黑板的宽度,然后心中情不自禁的做起了数学题,这在Flex
布局中也一样,有时候在一个页面中布局的内容的大小是后台返回的,但是页面通常需要均匀排布这些内容,这时候justify-content
属性就派上用场了,justify-content
决定了flex items
在主轴上的对齐方式,一共有六种取值如下图所示:
align-content
既然主轴上可以指定对齐方式,交叉轴当然也可以指定对齐方式,当在交叉轴上有多余的空间时,align-content
决定了多行flex-items
在交叉轴上的对齐方式(如果flex-warp:nowarp
,此属性将不起作用)。
重点说下
stretch
值,这个是默认的,当flex-items
在交叉轴上的size
为auto
时,则flex-items
所在的行会平分flex container
在交叉轴上剩余
的尺寸。回到上面flex-wrap
的例子,为什么3号方块为什么不是紧挨着1号的上边缘?这里是由于align-content
默认的是stretch
,即使flex-items
在交叉轴上明确的设定了高度,所以flex items
不会被拉伸,但是它们会排列在要被拉伸的位置,所以3号方块依然在被拉上后的起始位置上,当我们加上align-content: flex-start;
时则3号方块就会紧贴着1号方块的上边缘。
align-items
有的人天生就有较好的水平特性,随手在黑板上写一行字就能保证每个字的上边缘对齐,且在一条水平线上,好看极了,就像印刷的一样;而有些人则每行的字都对不齐,align-items
决定了单行flex items
在cross axis
(交叉轴)上的对齐方式。
当
flex items
在交叉轴方向上的size
(指width
或者height
,由交叉轴方向确定)为auto
时,stretch
会自动拉伸至填充;但是如果flex items
的size
并不是auto
,那么产生的效果就和设置为flex-start
一样。
注意
flex-start
和flex-end
都是相对于cross-start
和cross-end
而言的,而cross-start
和cross-end
的位置是受flex-wrap
影响的,默认值也是stretch
。
对比align-items和align-content的stretch
align-items
的stretch
是作用在每个flex item
上的,将flex item
在交叉轴上的size
设置为auto
则拉伸至这行所在的高度,这行所在的高度的确定是关键,并不是简单的认为是这行所在align-items
的最大值(就是手动设定的),还要考虑上align-content
的stretch
的影响,align-content
的stretch
会导致每行(注意这里是每行这个整体,就是以行为单位)平分剩余的空间(注意是剩余的空间,不是简单的认为是container
的高度),所以每行的高度是手动设定的这行最大的高度,加上这行align-content
的stretch
平分剩余空间的高度,可能读了这你还是不太理解,那么接下来进行实操讲解:
情形一
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8">
<title>Example of Perfect Center Alignment Using Flexbox</title>
<style>
.flex-container {
width: 500px;
min-height: 600px;
margin: 0 auto;
font-size: 22px;
border: 1px solid #666;
display: -webkit-flex; /* Safari 6.1+ */
display: flex; /* Standard syntax */
flex-wrap: wrap-reverse;
justify-content: space-evenly;
align-content: flex-start;
align-items: stretch;
}
.item {
width: 200px;
text-align: center;
font-size: 2em;
}
.item1{
height: 100px;
background-color: green;
}
.item2{
height: 50px;
background-color: red;
}
.item3{
height: 300px;
background-color: blue;
}
.item4{
height: auto;
background-color: #2c3e50;
}
</style>
</head>
<body>
<div class="flex-container">
<div class="item item1">1</div>
<div class="item item2">2</div>
<div class="item item3">3</div>
<div class="item item4">4</div>
</div>
</body>
</html>
解读:
由于这里设置的是align-content: flex-start
,所以每一行都是紧挨着的,由于每行不存在align-content
平分剩余的空间,所以第一行的最大高度是item1
设置的固定高度100px
,而第二行的最大高度则是item3
设置的固定高度,由于item4
设置的auto
所以要拉升至所在行的高度,最后就与item3
处在同一高度。
情形二
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8">
<title>Example of Perfect Center Alignment Using Flexbox</title>
<style>
.flex-container {
width: 500px;
min-height: 600px;
margin: 0 auto;
font-size: 22px;
border: 1px solid #666;
display: -webkit-flex; /* Safari 6.1+ */
display: flex; /* Standard syntax */
flex-wrap: wrap-reverse;
justify-content: space-evenly;
align-content: stretch;
align-items: stretch;
}
.item {
width: 200px;
text-align: center;
font-size: 2em;
}
.item1{
height: 100px;
background-color: green;
}
.item2{
height: 50px;
background-color: red;
}
.item3{
height: 300px;
background-color: blue;
}
.item4{
height: auto;
background-color: #2c3e50;
}
</style>
</head>
<body>
<div class="flex-container">
<div class="item item1">1</div>
<div class="item item2">2</div>
<div class="item item3">3</div>
<div class="item item4">4</div>
</div>
</body>
</html>
解读:
上面的代码只改动了一行align-content: stretch
会发现布局大不相同,首先由于这次align-content
设置的是stretch
,所以每行会平分剩余的高度,因为第一行固定设置的最大高度是item1
的100px
,第二行最大高度是item3
的300px
,所以剩下的高度是container
的600px - 300px -100px
等于200px
,所以每行获得剩下的100px
,最后第一行的高度是200px
,第二行的高度是400px
,所以3方块和1方块之间的空隙其实就是均分的100px
(这也是为什么align-content
设置为stretch
二行不紧挨着的原因),由于4号方块的高度设置的是auto
,所以会拉升至这行最后的高度至400px
。
align-self
flex items
可以通过align-self
覆盖flex container
设置的align-items
,默认值为auto
:默认遵从flex container
的align-items
设置。
.item4{
align-self: flex-end;
height: auto;
background-color: #2c3e50;
}
在上面的例子的item4
添加align-self: flex-end
后,则item4
不会进行拉伸至400px
,表现为起始点在最顶部,高度为字体的高度。
order
控件的展示顺序默认是按照在HTML
中书写的顺序来展示的,可以通过给每个控件设置order
值(可以为负数)可以自定义展示顺序,这个属性没啥好说的。
flex-grow
在主轴方向上,假如存在剩余的空间(或者空间不足)想分配给(收缩)每个元素,该以什么样的原则分配?因为Flex
的意义就是适配不同的屏幕,每个元素的大小会根据屏幕大小变化的,这个问题其实由三个属性决定的即flex-grow
,flex-shrink
和flex-basis
,首先说说flex-grow
吧,flex-grow
用来“瓜分”父项的“剩余空间”,就是空间有多余时,以怎么样的原则进行瓜分,其实就是百分比瓜分,默认值是0,接受一个正的值。
三个
item
都设置为1,则每个item
瓜分的剩余空间是相等的,假如第二个item
设置为2,则其瓜分的空间是另外二个的二倍,所以宽度也是二倍。
flex-shrink
flex-shrink
用来“吸收”超出的空间,有时候flex-item
设置的宽度太大,已经超出了container
的宽度,为了容下所有的item
,就要收缩一下,flex-shrink
就是定义怎么样收缩的,也是和flex-grow
一样按照百分比进行收缩,但是方式有点不太一样,要算上自己基础宽度来进行百分比,而flex-grow
则不用,具体看后面的例子。
flex-basis
flex-basis
是用来在分配在主轴上多余或者收缩空间之前指定flex-item
的基本尺寸,以这个尺寸来评价是否有剩余空间或者超出的部分。
-
auto
:默认值,设置为auto
之后,当有设置固定的size
时则basis
尺寸就等于这个固定的size
,当没有设置固定的size
时则等于自身内容的size
。 - 固定数值或者百分比:设置固定的数值或者百分比,则主轴上对应的
size(width/height)
就可忽略,basis
尺寸直接等于这个指定的固定值。
flex
flex是flex-grow
,flex-shrink
和flex-basis
的缩写,开发中经常使用flex
这个属性,最常用的就是flex:1
,其实就是flex:1 0 %0
分别表示flex-grow:1,flex-shrink:0,flex-basis:0%
,其默认值是flex:0 1 auto
。
flex演练
演练一
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8">
<title>Example of Perfect Center Alignment Using Flexbox</title>
<style>
.flex-container {
width: 500px;
min-height: 100px;
margin: 0 auto;
font-size: 22px;
border: 1px solid #666;
display: -webkit-flex; /* Safari 6.1+ */
display: flex; /* Standard syntax */
flex-wrap: wrap-reverse;
justify-content: space-evenly;
align-content: stretch;
align-items: stretch;
}
.item {
height: 100px;
line-height: 100px;
text-align: center;
font-size: 2em;
}
.item1{
flex: 1;
background-color: green;
}
.item2{
width: 100px;
background-color: red;
}
.item3{
width: 150px;
background-color: blue;
}
</style>
</head>
<body>
<div class="flex-container">
<div class="item item1">1</div>
<div class="item item2">2</div>
<div class="item item3">3</div>
</div>
</body>
</html>
解读:
首先计算每个item
的基本宽度,由于item设置flex: 1
也就是flex:1 0 0%
所以其basis
宽度为500px * 0% = 0px
,而item2
和item3
都没设置flex
则是默认值flex:0 1 auto
,由于item2
和item3
都指定了固定的width
尺寸,所以item2
的basis
尺寸为100px
,item3
的basis
尺寸为150px
,三个item
的basis
尺寸一共为250px
是小于container
的500px
的,所以表现为有剩余尺寸,这时候就要用到flex-grow
属性来决定剩余尺寸的分配,由于只有item1
的flex-grow
是设置有值的,所以item1
独享剩余的250px
,最后item1
的尺寸为0px+ 250px = 250px
,item2
的为100px + 0px = 100px
,item3
的尺寸为150px + 0px = 150px
。
演练二
.item1{
flex: 1;
background-color: green;
}
.item2{
width: 100px;
flex:2 0 30%;
background-color: red;
}
.item3{
width: 200px;
flex:3 0 80px;
background-color: blue;
}
首先还是计算每个
item
的基本宽度,item1
的basis
尺寸为0,item2
的basis
尺寸为500px * 30% = 150px
,item3
的为80px
,显而易见表现为有多余的270px
尺寸需要分配,item1
分配获得的尺寸为(1/(1+2+3))*270px = 45px
,item2
分配获得的尺寸为(2/(1+2+3))*270px = 90px
,item3
分配获得的尺寸为(3/(1+2+3))*270px = 135px
,最后item1
的尺寸为0 + 45px = 45px
,最后item1
的尺寸为150px + 90px = 240px
,最后item1
的尺寸为80 + 135px = 215px
。
演练三
.flex-container {
flex-wrap: nowrap;
}
.item1{
width: 100px;
background-color: green;
}
.item2{
width: 100px;
flex:2 2 60%;
background-color: red;
}
.item3{
width: 200px;
flex:3 1 300px;
background-color: blue;
}
上面将flex-container
的flex-wrap
设置为了nowrap
让三个item
只能在一行显示,item1
的basis
尺寸为100px
,item2
的basis
尺寸为300px
,item3
的basis
尺寸为300px
,显而易见表现为需要收缩,分配的收缩尺寸是700px-500px = 200px
。上文我们提到flex-shrink
属性不是简单的百分比,需要带上basis
尺寸,item1
的收缩尺寸为((100px * 1)/(100px * 1 + 300px * 2 + 300px *1)* 200px = 20px
,同理可以算得item2
的收缩尺寸为0.6*200 = 120px
,item3
的为0.3*200 = 60px
,所以最后item1
的尺寸为100px -20px = 80px
,item2
的为300px-120px = 180px
,item3
的为300px - 60px = 240px
。
思考
假如上面重新将flex-container
设置为wrap-reverse
结果会是怎样?这里不在进行分析,结果如下(最好自己自行计算一下):
Tips: 设置可以显示多行后,
item1
和item2
在一行表现为分配多余空间,item3
在一行也表现为分配多余空间。
九宫格
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8">
<title>Example of Perfect Center Alignment Using Flexbox</title>
<style>
.flex-container {
width: 1000px;
min-height: 800px;
margin: 0 auto;
font-size: 22px;
border: 1px solid #666;
display: -webkit-flex; /* Safari 6.1+ */
display: flex; /* Standard syntax */
flex-wrap: wrap;
justify-content: space-evenly;
/*align-items: stretch;*/
/*align-content: stretch;*/
}
.item {
/*为了让文字居中可以继续给item设置flex布局*/
display: flex;
align-items: center;
justify-content: center;
flex: 0 30.67%;
margin-bottom: 2%;
font-size: 2em;
background-color: green;
/*height: auto;*/
}
/*伪类取前三个*/
.item:nth-child(-n + 3) {
margin-top: 2%;
}
</style>
</head>
<body>
<div class="flex-container">
<div class="item">1</div>
<div class="item">2</div>
<div class="item">3</div>
<div class="item">4</div>
<div class="item">5</div>
<div class="item">6</div>
<div class="item">7</div>
<div class="item">8</div>
<div class="item">9</div>
</div>
</body>
</html>
Vertical stack
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8">
<title>Example of Perfect Center Alignment Using Flexbox</title>
<style>
.flex-container {
width: 1000px;
min-height: 800px;
margin: 0 auto;
font-size: 22px;
border: 1px solid #666;
display: -webkit-flex; /* Safari 6.1+ */
display: flex; /* Standard syntax */
flex-wrap: nowrap;
flex-direction: column;
justify-content: space-evenly;
/*align-items: stretch;*/
}
.item {
/*为了让文字居中可以继续给item设置flex布局*/
display: flex;
align-items: center;
justify-content: center;
height: 150px;
font-size: 2em;
background-color: green;
}
</style>
</head>
<body>
<div class="flex-container">
<div class="item">1</div>
<div class="item">2</div>
<div class="item">3</div>
<div class="item">4</div>
</div>
</body>
</html>
实现这个效果试了好长时间,看起来简单,其实不然,首先
item
是没有设置交叉轴的尺寸的,也就是宽度是默认的auto
,由于flex-warp
设置的是nowarp
单行显示,单行显示下交叉轴尺寸如果是auto
则这行的交叉轴尺寸就是container
的尺寸,也就是此时每行的宽度就是container
的宽度(单行设置align-content
是没有效果的),由于align-items
默认的是stretch
,当size
为auto
时会拉升至这行所在的尺寸,所以会在宽度方向上实现铺满效果。
假如我们将上面的item
的宽度写死为width: 500px
,则此时align-item:stretch
则表现为flex-start
整体左对齐,此时想让内容水平居中,设置align-contetn: center
不起效果,需要设置align-items:center
才行。
网友评论