阅读w3c规范中flex部分,并对flex尺寸计算和对齐计算过程进行重点记录,其中重点解释flex-basis
和align-content
,也会涉及flex
、auto margin
、flex lines
等概念。
首先要需要了解:flex布局先计算尺寸,再进行布局。
计算尺寸
除了设置尺寸属性外,flex-basis
也决定元素在flex容器中占据的尺寸。
flex-basis
// flex-basis
the initial main size of the flex item, before free space is distributed according to the flex factors.
在自由空间根据弹性系数进行分配之前,flex item的初始主尺寸(又有基准尺寸这一说法)。
flex-basis有三种取值:
- auto
- content
- width:计算方式和盒模型中的
width
或height
计算方式相同
其默认值是:0。
flex-basis
设置为0,则初始主尺寸为0,那么按1 1 2
分配的话,三个flex item的总尺寸比是1:1:2
。
flex-basis为auto时表现却不一样。
看看flex-basis:auto
的解释:
When specified on a flex item, the auto keyword retrieves the value of the main size property as the used flex-basis. If that value is itself auto, then the used value is content.
auto
值使得尺寸计算会检索并使用flex item的main size property
(width
或height
)作为其flex-basis的值,如果flex元素本身的尺寸是auto
(如不显式指定尺寸),则取值为元素内容的尺寸。
在空间分配初始,根据总空间 - flex-basis空间 = 剩余可分配空间
计算剩余空间。再根据flex-grow
和flex-shrink
对剩余空间进行分配。
故第一种情况下,内容空间均为0,剩余空间分配后,三者占比为1:1:2
。第二种情况,内容空间为内容的尺寸
,所以剩余空间分配后是1:1:2
,三个flex item所占的主轴空间并不等于是1:1:2
。
如果同时显式指定flex-basis和width,会发生什么呢?
// html
<div class="wrp">
<div class="item item1">short</div>
<div class="item item2">loooooooooog</div>
<div class="item item3">short</div>
</div>
// css
.wrp {
border: 1px solid black;
display: flex;
flex-wrap: nowrap;
width: 400px;
}
.wrp .item {
border: 1px solid grey;
box-sizing: border-box;
/* flex-basis: 0; */
flex-basis: auto;
}
.wrp .item1 {
width: 100px;
}
flex-basis: 0
时:
flex-basis: auto
时:
flex-basis: auto
时,通过css设置的宽度才会被考虑,此时.item1
的宽度才明显宽于.item3
。
flex
flex
属性是对<flex-grow> <flex-shrink> <flex-basis>
的简写,其取值包括
- initial: 0 1 auto.
- auto: 1 1 auto
- none: 0 0 auto
- positive-number: positive-number 1 0
最后一种常取值为1,例如将某一个flex item设置flex: 1
,其他弹性元素不进行设置,那么这个元素将占用所有的剩余空间。
Alignment
当flex容器结束flex item layout且所有子元素的尺寸都确定后,它们将在容器内部进行对齐。
除了flex及flex item的对齐属性外,其他属性也会影响元素的展现位置,例如marign
、绝对定位、浮动等。绝对定位、浮动都会使元素脱离flex布局流,也就是成为了非flex item
,所以对齐时计算分配空间时,也不会考虑这部分元素。margin
属性的效果则不同。
align by auto margin
规范特别提到为一个flex item设置margin: auto
属性。
设置margin: auto
,则:
- 在计算
flex base和flexible length
时,auto margin被视为0。 - 在通过justify-content和align-self进行对齐之前,任何正向可用空间都将分配给该维度中的auto margin。
- 忽略溢出的盒子的自动边距 并向结束方向溢出。
利用上述特型,我们很容易就能实现典型的导航结构:
代码:
// html
<nav>
<ul>
<li><a href=/about>About</a>
<li><a href=/projects>Projects</a>
<li><a href=/interact>Interact</a>
<li id="login"><a href=/login>Login</a>
</ul>
</nav>
// css
nav > ul {
display: flex;
border: 1px solid black;
}
nav > ul > li {
width: 100px;
}
nav > ul > #login {
margin-left: auto;
}
当设置最后一个元素margin: auto
后,所有的剩余空间先分配给它。因此flex对齐属性justify-content
对该元素无效。
如果margin
设置为数值,从样式表现来看,margin
值会影响剩余空间的分配,此时margin
会被纳入content尺寸的计算。具体计算公式和计算时机目前还没有找到。。
需要注意的是!弹性布局容器中的margin
是不折叠的。
justify-content、align-items和align-self
它们在所有弹性长度和自动边距都确定后才进行对齐计算,也能控制溢出 flex lines的元素。flex lines在下文会讲到。
它们的优先级低于margin
,设置auto margin后,它们就失效了。
// html
<nav>
<ul>
<li><a href=/about>About</a>
<li><a href=/projects>Projects</a>
<li><a href=/interact>Interact</a>
<li id="login"><a href=/login>Login</a>
</ul>
</nav>
// css
nav > ul {
display: flex;
border: 1px solid black;
height: 100px;
justify-content: center;
align-items: flex-start;
width: 500px;
}
nav > ul > li {
width: 100px;
}
nav > ul > #login {
margin-left: auto;
margin-top: auto;
}
align-content
说到align-content
,必须和align-items
做个对比。
// align-content:
The align-content property aligns a flex container’s lines within the flex container when there is extra space in the cross-axis, similar to how justify-content aligns individual items within the main-axis. Note, this property has no effect on a single-line flex container.
// align-items:
align-items sets the default alignment for all of the flex container’s items, including anonymous flex items.
align-items
和justify-content
类似,不同在于前者设置容器中的items在交叉轴上的对齐方式。
align-content
适用于multi-line
的flex容器,当交叉轴有多余空间时,它设置容器内flex lines的对齐方式,和justify-content在主轴上对齐单个元素类似。
要理解定义,还需要了解Flex lines
和multi-line
。
flex lines和multi-line
flex lines
可谓是flex布局的重点。
弹性布局容器中的元素沿着flex lines对齐,若干虚拟container被布局算法用来分组和对齐(平行于主轴方向)。一个弹性布局容器要么是单行要么是多行,取决于flex-wrap
属性。
一个单行容器(如:
flex-wrap: nowrap
)将所有子节点放在同一行中,即使这可能导致其内容溢出。一个多行容器(如:
flex-wrap:wrap
或flex-wrap:wrap-reverse
)将其flex item分为多行,类似于文本因为太宽无法适应当前行而被分到新的一行展示一样。当额外的line被创建后,根据flex-wrap属性,容器将它们沿着容器内的交叉轴堆放。每一行都包含至少一个flex item,除非弹性容器本身是完全empty的。
一旦内容被分为多行,每行的布局就是独立的。弹性长度和justify-content
以及align-self
属性一次只考虑单一一条flex line上的items。
在多行flex容器中(即使只有一行),每一行的交叉轴尺寸都是包含行上的弹性项目所需的最小尺寸(在align-self
对齐之后),并且交叉轴上的lines使用align-content属性在flex容器中对齐。
在单行flex容器中,line的交叉轴尺寸是flex容器的交叉轴尺寸,align-content不起作用。line的主轴尺寸总是和flex容器的内容盒子的尺寸相同。
举个例子:
<div class="wrp wrp1">
<div>first line</div>
<div>second line</div>
</div>
<div class="wrp wrp2">
<div>first line</div>
<div>second line</div>
</div>
<div class="wrp wrp3">
<div>first line</div>
<div>second line</div>
</div>
// css
.wrp {
border: 1px solid black;
display: flex;
flex-wrap: wrap;
height: 80px;
width: 100px;
margin-bottom: 10px;
}
.wrp > div {
width: 100%;
height: 20px;
border: 1px solid grey;
}
.wrp1 {
align-items: center;
}
.wrp2 {
align-content: center;
}
.wrp2 div:first-child {
align-self: flex-start;
}
.wrp3 {
flex-wrap: nowrap;
align-items: flex-start;
}
.wrp3 div:first-child {
align-self: flex-end;
}
.wrp2
中,根据multi-line弹性布局容器中每条交叉轴上的flex line的尺寸是包含弹性项目所需的最小尺寸
原则,那么容器中的两个交叉轴上的line将都是20,所以设置align-content: center
后,二者始终并排居中。
虽然.wrp1
也是一个多行弹性布局容器,但.wrp1
没有显式指定align-content
(其默认值为stretch
),而指定了align-items:center
。看stretch
的定义:
Lines stretch to take up the remaining space. If the leftover free-space is negative, this value is identical to flex-start. Otherwise, the free-space is split equally between all of the lines, increasing their cross size.
align-content: stretch
使得flex lines拉伸以占据剩余空间。如果剩余空闲空间为负值,则此值与flex-start
相同。否则,自由空间会在所有行之间平均分配,从而增加它们的在交叉轴的尺寸。
所以.wrp1
中每条flex line在交叉轴上的尺寸都是: flex容器的尺寸 / 2 = 40
。根据align-items
和justify-content
相似的定义,可以推测这个属性一次也只考虑以单一一条flex line上的items。所以.wrp1
中的每行flex line中的flex item都在其line上垂直居中。
.wrp3
属于单行容器,所以其flex line占据中交叉轴的全部尺寸。
最后,对Alignment内容总结,有如下要点:
- flex布局容器中,由
flex line
决定元素对齐位置的范围。 - 多行flex lines情况下,每行都有独立的布局范围,互不影响,其交叉轴尺寸默认是
包含弹性项目所需的最小尺寸
。 - 单行flex line容器的flex line尺寸和交叉轴尺寸相同。
总结
- flex布局通过
display: flex
指定。其子元素会形成类似inline-block
块。 - flex容器内部没有
margin折叠
,auto margin计算优先于flex的对齐属性。 - 理解flex lines的概念很重要。
flex-wrap: nowrap
的容器是single-line
,而flex-wrap: wrap
则导致出现multi lines container
,每个flex-item的尺寸计算基于其所在的line。