CSS探索系列之flex布局
当对一个文档进行布局的时候,浏览器的渲染引擎会根据标准之一的 CSS 基础框盒模型(CSS basic box model),将所有元素表示为一个个矩形的盒子(box)。每个盒子由四个部分组成:内容 Content
、内边距 Padding
、边框 Border
、外边距 Margin
。CSS 决定这些盒子的大小、位置以及属性(例如颜色、背景、边框尺寸…)。
布局的传统解决方案,基于盒状模型,依赖 display
属性 + position
属性 + float
属性。它对于那些特殊布局非常不方便,比如,垂直居中就不容易实现。
Flex
是 Flexible Box
的缩写,意为"弹性盒子",用来为盒状模型提供最大的灵活性。
弹性盒子布局(Flexible Box Layout)定义了一种针对用户界面设计而优化的 CSS
盒子模型。在弹性布局模型中,弹性容器的子元素可以在任何方向上排布,也可以弹性伸缩其尺寸,既可以增加尺寸以填满未使用的空间,也可以收缩尺寸以避免父元素溢出。子元素的水平对齐和垂直对齐都能很方便的进行操控。
基本概念
弹性盒子布局是一种一维的布局模型,一次只能处理一个维度上的元素布局,一行或者一列。作为对比的是另外一个二维布局 网格布局,可以同时处理行和列上的布局。
.container {
display: flex | inline-flex; /**生成块或内联的弹性容器**/
}
弹性容器: display
属性的值为 flex | inline-flex
的元素。
弹性项目:弹性容器的每个子元素都称为弹性项目。
轴:每个弹性盒子布局包含两个轴。弹性项目沿其依次排列的那根轴称为主轴。垂直于主轴的那根轴称为侧轴(cross axis)。
方向:弹性容器的主轴起点(main start)/主轴终点(main end)和侧轴起点(cross start)/侧轴终点(cross end)描述了弹性项目排布的起点与终点。它们具体取决于弹性容器的主轴与侧轴中,由 writing-mode
确立的方向(从左到右、从右到左,等等)。
行:根据 flex-wrap
属性指定弹性项目单行显示还是多行显示 。如果允许换行,这个属性允许你控制行的堆叠方向(从左到中、从右到左)。
尺寸:根据弹性容器的主轴与侧轴,弹性项目的宽和高中,对应主轴的称为主轴尺寸(main size) ,对应侧轴的称为 侧轴尺寸(cross size)。
由于弹性盒子使用了不同的布局算法,某些属性用在弹性容器上没有意义:
- 多栏布局模块的
column-*
属性对弹性项目无效。 float
与clear
对弹性项目无效。使用float
将使元素的display
属性计为block
。vertical-align
对弹性项目的对齐无效。
容器的属性
以下6个属性设置在容器上:
flex-direction
flex-wrap
flex-flow
justify-content
align-items
align-content
flex-direction属性
定义了主轴的方向(正方向或反方向),即弹性项目的排列方向。
初始值:row
取值:
row
:主轴为水平方向,从左到右。row-reverse
:主轴为水平方向,从右到左。column
:主轴为垂直方向,从上往下。column-reverse
:主轴为垂直方向,从下往上。
请注意:
row
和row-reverse
受 flex 容器的direction
属性的影响。
css
属性direction
用来设置文本、表列水平排列的方向,初始值ltr
。ltr
表示从左到右 (类似汉语、英语等大部分语言),rtl
表示从右到左 (类似希伯来语或阿拉伯语)。
direction
属性是ltr
,row
表示从左到右定向的水平轴,而row-reverse
表示从右到左; 如果dir
属性是rtl
,row
表示从右到左定向的轴,而row-reverse
表示从左到右。
column
和column-reverse
受弹性容器的writing-mode
属性的影响。
writing-mode
属性指定块流动方向,即块级容器堆叠的方向,以及行内内容在块级容器中的流动方向。
horizontal-tb
:行内元素从左到右或从右到左(受direction
属性的影响) ,块元素由上往下。vertical-rl
:行内元素由上往下或由下往上,块元素从左到右或从右到左。(受direction
属性的影响)vertical-lr
:行内元素由上往下或由下往上,块元素从左到右或从右到左。(受direction
属性的影响)
flex-wrap属性
指定弹性项目单行显示还是多行显示 。如果允许换行,这个属性允许你控制行的堆叠方向。
初始值:nowrap
取值:
nowrap
:弹性项目被摆放到到一行,这可能导致溢出弹性容器。 cross-start 会根据 flex-direction 的值 相当于 start 或 before。wrap
:弹性项目被打断到多个行中,行由上往下排列。wrap-reverse
:和wrap
的行为一样,但是行由下往上排列。
flex-flow属性
flex-flow
属性是 flex-direction
和flex-wrap
的简写。
初始值:row nowrap
。
flex-flow: column-reverse wrap;
justify-content属性
定义了弹性项目在主轴上的对齐方式及其周围的空间的分配。
初始值:flex-start
取值:
flex-start
:主轴起点对齐。flex-end
:主轴终点对齐。center
: 居中。space-between
:两端对齐,弹性项目之间的间隔都相等。space-around
:每个弹性项目两侧的间隔相等。所以,项目之间的间隔比项目与边框的间隔大一倍。space-evenly
:弹性项目之间、弹性项目与边框之间的间隔都相等。
以上六个是常见取值,还有值start、left、right、baseline、first baseline、last baseline、stretch、safe、unsafe
。??
align-items属性
属性定义弹性项目在侧轴上如何对齐。
初始值:normal
。取决于我们处在什么布局模式中
取值:
flex-start
:侧轴起点对齐flex-end
:侧轴终点对齐center
:中间对齐baseline
: 弹性项目的第一行文字的基线对齐stretch
:弹性项目将占满整个容器的高度(弹性项目未设置高度或设为auto
)。
align-content属性
当弹性盒子分多行时,定义了行的对齐方式和周围分配空间。该属性对单行(即:未设置flex-wrap
或 flex-wrap: nowrap
)弹性盒子模型无效。
取值:
flex-start
:侧轴起点对齐flex-end
:侧轴终点对齐center
:中间对齐space-between
:与侧轴两端对齐,轴线之间的间隔平均分布。space-around
:每根轴线两侧的间隔都相等。所以,轴线之间的间隔比轴线与边框的间隔大一倍。stretch
(默认值):轴线占满整个侧轴(弹性项目未设置高度或设为auto
)。
place-content属性
是 align-content
和 justify-content
的简写。第一个值为 align-content
属性, 第二个值为justify-content
。
place-content: center start;
非常重要:如果没有设置第二个值,那么第二个的值与第一个相等,此前提是第一个值对两个属性都是有效的。如果设置的这个值对两个属性都无效,那么整个设置的值就是无效的。
项目的属性
以下6个属性设置在项目上:
order
flex-grow
flex-shrink
flex-basis
flex
align-self
order属性
定义项目的排列顺序。数值越小,排列越靠前,默认为0。
flex-grow属性
指定了弹性项目扩展规则,即弹性容器中剩余空间的应该分配多少给弹性项目。
剩余的空间是弹性容器的主轴尺寸减去所有弹性项目的尺寸和。
默认为0
,即如果存在剩余空间,也不放大;大于0,则弹性容器剩余空间的分配就会发生;负值无效。
具体规则如下:
- 所有剩余空间总量是1
- 只有一个弹性项目设置了
flex-grow
属性值- 如果
flex-grow
值小于1,则扩展的空间就是总剩余空间 * flex-grow值
- 如果
flex-grow
值大于1,则独享所有剩余空间
- 如果
有多个弹性项目设置了
flex-grow
属性值如果
flex-grow
值总和小于1,则每个子项扩展的空间就是总剩余空间 * flex-grow值
。如果
flex-grow
值总和大于1,则所有剩余空间被利用,分配比例就是flex-grow
属性值的比例。如:所有弹性项目都有相同的
flex-grow
系数,那么所有的项目将获得相同的剩余空间。如:三个弹性项目,有一个的
flex-grow
属性为2,其他项目都为1,即:1:2:1
,则值为2
的占据剩余空间的2/4
,其他两个分别占1/4
。
flex-shrink属性
指定了弹性项目的收缩规则。弹性项目仅在默认宽度之和大于容器,且未设置flex-wrap
或者flex-wrap: nowrap
的时候才会发生收缩。
不足空间是所有弹性项目的宽度和 - 弹性容器的宽度
。
默认值为1,即如果空间不足,弹性项目将缩小;值为0
表示不收缩;负值无效。
具体规则:
- 只有一个弹性项目设置了
flex-shrink
flex-shrink
值小于1,则收缩的尺寸不完全,会有一部分内容溢出弹性容器。flex-shrink
值大于等于1,则收缩完全,正好填满弹性容器。
多个弹性项目设置了
flex-shrink
flex-shrink
值的总和小于1,则收缩的尺寸不完全,每个弹性项目的收缩尺寸为不足空间 * flex-shrink值
。flex-shrink
值的总和大于1,则收缩完全,每个弹性项目收缩尺寸的比例和flex-shrink
值的比例一样。如:所有项目的
flex-shrink
属性都为1,当空间不足时,都将等比例缩小。如:一个弹性项目的
flex-shrink
属性为0,其他项目都为1,则空间不足时,前者不缩小。
flex-basis属性
指定了弹性项目在主轴方向上的初始大小。浏览器根据这个属性,计算主轴是否有多余空间。
注意:弹性项目的最终尺寸,是由一系列盒模型属性和布局算法决定的,具体可查看下文【弹性项目尺寸的计算】章节。
初始值:auto
取值:
auto
,即弹性项目的本来大小。最初
auto
的含义是 "参照width
和height
属性"。在此之后,
auto
的含义变成了自动尺寸, 而main-size
变成了 "参照width
和height
属性"。实际执行于 bug 1032922.然后呢,这个更改又在 bug 1093316 中被撤销了, 所以
auto
变回了原来的含义;而一个新的关键字content
变成了自动尺寸。 (bug 1105111 包括了增加这个关键字)。<width>
可以跟
width
或height
属性的值一样(比如350px、2rem、30%),则弹性项目将占据固定空间。负值是不被允许的。Note: 当一个元素同时被设置了
flex-basis
(除值为auto
外) 和width
(或者在flex-direction: column
情况下设置了height
) ,flex-basis
具有更高的优先级。content
基于弹性项目的内容自动调整大小。除火狐,其他大多数浏览器不支持。
其他值。除火狐,其他大多数浏览器不支持。
/* 固有的尺寸关键词 */ flex-basis: fill; flex-basis: max-content; flex-basis: min-content; flex-basis: fit-content;
flex属性
flex
属性是flex-grow
, flex-shrink
和 flex-basis
的简写,后两个属性可选。
建议优先使用这个属性,而不是单独写三个分离的属性,因为浏览器会推算相关值。
默认值:initial
,0 1 auto
。
flex
属性可以指定1个,2个或3个值:
单值语法:值必须为以下其中之一
一个无单位数值:它会被当作
<flex-grow>
的值。一个有效的宽度(width)值:它会被当作
<flex-basis>
的值。关键字
none
,auto
或initial
。auto
:1 1 auto
none
:0 0 auto
initial
:0 1 auto
。即:flex-grow、flex-shrink、flex-basis
的默认值。
双值语法:第一个值必须为一个无单位数值,当作
<flex-grow>
的值。第二个值必须为以下之一一个无单位数:它会被当作
<flex-shrink>
的值。一个有效的宽度值:它会被当作
<flex-basis>
的值。
三值语法
第一个值必须为一个无单位数,并且它会被当作
<flex-grow>
的值。- 第二个值必须为一个无单位数,并且它会被当作
<flex-shrink>
的值。 - 第三个值必须为一个有效的宽度值, 并且它会被当作
<flex-basis>
的值。
align-self属性
align-self
属性允许单个项目有与其他项目不一样的对齐方式,可覆盖align-items
属性。
初始值:auto
,表示继承父元素的align-items
属性,如果没有父元素,则等同于stretch
。
取值:可能取6个值,除了auto
,其他都与align-items
属性完全一致。
弹性项目尺寸的计算
在弹性盒子布局中,一个弹性项目的最终尺寸是基础尺寸、弹性增长或收缩、最大最小尺寸限制共同作用的结果:
- 基础尺寸:由CSS
flex-basis、width
等属性,内容尺寸以及box-sizing
盒模型共同决定; - 弹性增长指的是
flex-grow
属性,弹性收缩指的是flex-shrink
属性; - 最大最小尺寸限制指的是
min-width
/max-width
等CSS属性,以及min-content
最小内容尺寸。
#优先级
最大最小尺寸限制 > 弹性增长或收缩 > 基础尺寸
弹性项目尺寸的计算规则:
- 如果没有设置
flex-basis
属性,那么flex-basis
的大小就是弹性项目的width
属性的大小; - 如果没有设置
flex-basis
、width
属性,那么flex-basis
的大小就是弹性项目内容的大小; - 默认情况下,
flex-basis
的大小不会收缩至小于内容尺寸。若想改变这一状况,可以设置min-width、width
属性。 - 主轴空间有剩余空间,遵循
flex-grow
规则;空间不足,遵循flex-shrink
规则
以下在实际开发的项目中遇到的问题:使用了flex
布局后,弹性项目中文本溢出自动省略号(...)的属性(即, text-overflow:ellipsis
)失效。
<section>
<div class="box_01">
<div class="item_01">低眉含笑间</div>
<div class="item_01 item_01_text01" style="flex: 1; ">
<div class="text">寒灯纸上梨花雨凉 谁的深情绚烂了三生石上的一见倾心 灯烛依旧 无人执手。低眉含笑间 寒灯纸上梨花雨凉 谁的深情绚烂了三生石上的一见倾心 灯烛依旧 无人执手
</div>
</div>
</div>
</section>
<style>
.box_01 {
display: flex;
padding: 10px;
border: 1px solid #0aa;
}
.box_01 .item_01 {
margin-right: 15px;
padding: 10px;
color: #fff;
text-align: center;
background-color: #0aa;
white-space: nowrap;
}
.box_01 .item_01 .text {
overflow: hidden;
white-space: nowrap;
text-overflow: ellipsis;
}
.box_01 .item_01_text01 {
min-width: 0;
}
/*
.box_01 .item_01_text02 {
overflow: hidden;
white-space: nowrap;
text-overflow: ellipsis;
}
*/
</style>
从本质上来说,ellipsis
失效的是因为.text
元素中的文本没有溢出。
.text_parent
元素是一个弹性项目,设置了flex: 1
(即:flex-grow:1; flex-shrink:1; flex-basis: auto;
,flex-basis: auto;
),且没有设置width
属性,它的宽度由内容宽度决定,即.text
元素的宽度。
.text
元素默认情况下width: 100%
,宽度与父元素.text_parent
一样。父元素宽度不固定的情况下,.text
元素的宽度取内容宽度,即文本的宽度。
最终结果:.text_parent
的宽度 = .text的宽度
= 文本宽度,所有文本没有溢出
要使ellipsis
启效,首先我们可以想到的方法是给.text
元素设置一个小于文本宽度的width
值,但这种方法显然不适用于响应式开发。
另一种思路是:给.text_parent
指定一个小于文本宽度的width
值。
方法一:.text_parent
设置overflow
属性
.text_parent {
overflow: hidden;
}
弹性项目在弹性容器中,有一个可用宽度。设置overflow
后,表示弹性项目宽度不超出可用宽度。
方法二:.text_parent
设置width
属性
.text_parent {
width: 0;
}
弹性项目的width
属性的作用是告诉浏览器:我有自己的宽度,不需要取内容的宽度
。注意:弹性项目的最终尺寸不一定是css中的width
值,它会根据一系列的盒模型属性来计算。
方法三:.text_parent
设置min-width
属性
/**
根据CSS规范草案,一般情况下min-width属性默认值是0,但弹性项目的min-width属性默认值是auto,这样能为弹性盒子布局指明更合理的默认表现。
**/
.text_parent {
min-width: 0;
}
弹性项目设置了min-width
属性,浏览器将采用如下算法(shrink-to-fit算法
??):
弹性项目宽度 = min ( max (最小宽度, 可用宽度) , 首选宽度)
最小宽度:min-width
的值
可用宽度:弹性项目在弹性容器中的允许宽度
首选宽度:内容不发生换行的时的宽度。(示例中的文本宽度)
以上是个人对弹性项目width
计算的理解,如有不对,敬请指正。
参考文章:
张鑫旭 - Oh My God,CSS flex-basis原来有这么多细节
浏览器前缀
flex
布局需要一些浏览器前缀来最大力度地兼容大多数的浏览器。Flex
布局的前缀不只是在属性前面添加浏览器前缀,不同浏览器下的属性名和属性值都不同,这是因为Flexbox
布局的标准一直在变,一共有old, tweener, new
三个版本。
可能处理前缀的最好方法是使用新的语法书写CSS并通过Autoprefixer运行CSS,能够很好地处理这个问题。
另外,这里有一个Sass中 @mixin 来处理一些前缀,也可以给你一些处理前缀的启发:
@mixin flexbox() {
display: -webkit-box;
display: -moz-box;
display: -ms-flexbox;
display: -webkit-flex;
display: flex;
}
@mixin flex($values) {
-webkit-box-flex: $values;
-moz-box-flex: $values;
-webkit-flex: $values;
-ms-flex: $values;
flex: $values;
}
@mixin order($val) {
-webkit-box-ordinal-group: $val;
-moz-box-ordinal-group: $val;
-ms-flex-order: $val;
-webkit-order: $val;
order: $val;
}
.wrapper {
@include flexbox();
}
.item {
@include flex(1 200px);
@include order(2);
}
相关问题
iPhone 设备兼容
iPhone 11pro、iPhone 不支持 justify-content: right
,请用 justify-content: flex-end
代替。