背景
咱们常常在某些业务场景中,须要实现子元素均等占用父元素空间的成果。到目前为止,我应用最多的就是用 flex: 1
来实现。直到有一天我忽然想应用 flex-grow: 1
来达到雷同的成果,然而浏览器却给了我不一样的体现,我才发现我疏忽了 残余空间(free space)这个关键词。始终以来,我是以 flex container 的宽度为根底来思考空间调配的问题,简略得将 flex: 1
与 flex-grow: 1
划上了等号。
本文是重新学习了 flexbox 之后做的总结,次要围绕 flex
,flex-basis
,flex-grow
以及 flex-shrink
这 4 个属性。
在 Firefox 审查 flexbox 布局的节点能够看到一些尺寸数据,然而 Chrome 如同并不能,所以文中的代码都会在 Firefox 中审查,我的 Firefox 版本是 65.0.2。
flexbox 基本概念
对于一些基础知识,其余文章曾经有具体的阐明,本文就只是简略表述一下。
- 首先,设置了
display: flex
的元素会成为一个 flex container,并创立一个 FFC(Flex Formatting Context),而该元素自身则会体现为一个 块级元素 ,相应的,设置了inline-flex
的元素则体现为 内联元素。 - flex container 外部的元素会成为 flex item,包含外部的纯文本(会被包裹一个匿名块级盒子)。相对定位元素因为不会参加到 flex 布局中来,所以不会成为一个 flex item。
- 最初,在进行 flex layout 时,
flex
会失效,使 flex item 齐全填充至 flex container 的可用空间,或者膨胀 flex item 以阻止溢出。当所有的 flex item 确定了尺寸之后,就会依据justify-content
、align-self
等属性进行排列和布局。
flex-basis
flex-basis
会代替 flex item 在主轴(main axis)方向上的尺寸属性(width/height),并在 调配残余空间 (_free space_) 之前 初始化 flex item 的主轴尺寸。
承受的值与 width
或者 height
雷同,比方数值、百分比、auto
、min-content
以及 max-content
,另外还有 content
。
auto
被指定为 auto
时,会取以后 flex item 主轴尺寸属性(width/height)的值,如果取到的值是 auto
时,则会应用 content
,也就是依据 flex item 的内容大小来确定。比方以下构造:
<div style="display: flex; width: 200px; border: 1px dotted #333;">
<div id="first" style="background: #eee; width: 80px; flex-basis: auto;">
short text
</div>
<div id="second" style="background: #ccc; flex-basis: auto;">
very looooooooong text
</div>
</div>
应用 Firefox(反对 content
,然而 min-content
和 max-content
是有效值)审查节点的时候能够看到:
<img width=”391″ alt=” 根底尺寸 ” src=”https://user-images.githubusercontent.com/13151172/91567558-be38e080-e977-11ea-83f5-2a6af5a3be01.png”>
<img width=”391″ alt=” 内容尺寸 ” src=”https://user-images.githubusercontent.com/13151172/91567585-bf6a0d80-e977-11ea-9cae-b18eaa288c33.png”>
因为设置了 width: 80px
,所以 div#first
的根底尺寸是 80px,而 div#second
的宽度是 auto
,所以它的根底尺寸就基于它的内容失去,即内容尺寸。
content(兼容性不好)
被指定为 content
时,是依据 flex item 的内容大小来确定尺寸的。相当于应用了 width: max-content
。Chrome 是不反对该值的,Firefox 能够应用该值。
flex-grow
简略讲,flex-grow
是指以后 flex item 能够从 flex container 的残余空间(positive free space)里调配到多少比例。
这里有一个关键词是positive free space,而我之前误会成了 flex container 的整个 content box。
例如 flex-grow: 1
就是指心愿调配到 100% 的 positive free space,flex-grow: 0.5
就是指 50%,以此类推。所以,当所有的 flex-grow
总和小于 1 时,这些 flex items 也就没有调配到 100% 的 positive free space,就会有空间被剩下;当所有的 flex-grow
总和大于 1 时,就会以 100% 为根底从新计算比例,毕竟不会有超过 100% 的空间能够调配。
flex-shrink
MDN 中的定义:
flex-shrink
属性指定了 flex 元素的膨胀规定。flex 元素仅在默认宽度之和大于容器的时候才会产生膨胀,其膨胀的大小是根据flex-shrink
的值。
能够用 negative free space 来示意溢出的空间。与 flex-grow
不同的是,如果指定 flex-shrink: 1
,不能简略的了解为这个 flex item 心愿调配到 100% 的 negative free space。这里的 1 相当于一个权重,具体的计算过程会在如何计算弹性中形容。
在膨胀规定中,有一个概念是 content-based minimum size,直译过去是 基于内容的最小尺寸 。它限度了 flex item 能够膨胀的水平,不会让 flex item 膨胀到更小的尺寸。它的值可能是一个确定的 主轴(main axis)尺寸属性值(width/height),也可能是 min-content
。这个概念会在如何计算弹性中应用到。
flex
flex: 0 1 auto
是它的初始值。也就是说 flex item 在默认状态下,能够在主轴维度上放大但不能拉伸,并且依据 width/height
来确定其大小。
而被我误会为等价于 flex-grow: 1
的 flex: 1
,残缺的值是 flex: 1 1 0
(flex: <positive-number> 1 0
)。
如何计算弹性
<div style="display: flex; width: 200px; border: 1px dotted #333;">
<div id="first" style="background: #eee;">
short text
</div>
<div id="second" style="background: #ccc; width: 280px;">
very looooooooong text
</div>
</div>
以上述代码为例:
步骤一
确定主轴(main axis)的可用空间(available space),也就是 flex container 的 content box,可能是一个确定的尺寸,也可能须要计算得出。
例子中的可用空间就是 200px。
步骤二
依据 flex-basis
确定每一个 flex item 的 根底尺寸(flex base size)。
因为 div#first
的 flex-basis
和 width
都为 auto
,所以他的根底尺寸就是它的内容尺寸,通过 Firefox 审查节点能够看到它的内容尺寸是 59.93px。而 div#second
因为设置了 width: 280px
,所以 280px 就是它的根底尺寸。
另外,在这一步骤中,最大最小束缚(这个例子是 min-wdith
和 max-width
)会被疏忽。如果给 div#first
一个 min-width: 100px
,它的根底尺寸仍然是 59.93px。至于这些束缚何时失效,能够在前面的步骤看到。
步骤三
计算 flex item 的主轴尺寸(main size)总和,确定应用 flex-grow
还是 flex-shrink
。
例子中,能够失去总和是 339.93px,超过了 flex container 的尺寸,所以能够确定 flex-shrink
失效。
步骤四
__确定 positive free space 或者 negative free space 的大小__。
首先,须要计算例子中 flex-shrink
的总和,失去值为 2。因为大于 1,所以 negative free space 的值就是 339.93px – 200px = 139.93px。
步骤五
为每个 flex item 调配 free space,确定批改后的尺寸。
因为在这个例子中失效的是 flex-shrink
属性,所以每个 flex item 的最终大小是根底尺寸减去它所调配到的 negative free space。那么,每一个 flex item 应该被调配到多少呢?
<img width=”818″ alt=” 公式 ” src=”https://user-images.githubusercontent.com/13151172/91580974-75d1f080-e980-11ea-9821-1d5ecc031f1f.png”>
在这个公式里,能够看到根底尺寸与 flex-shrink
的值做了相乘,也就是上文 flex-shrink 中说到的权重。能够简略套用这个公式:
div#first
: (339.93 – 200) * 59.93 / (59.93 + 280) = 24.67pxdiv#second
: (339.93 – 200) * 280 / (59.93 + 280) = 115.26px
所以最初 div#first
的宽度是 35.27px,div#second
是 164.73px。在这个例子中,以上就是最终尺寸了,能够在 Firefox 审查节点看到这个后果。
然而,如果将 div#second
的宽度调整到 1000px
,就会有接下来的这一步,咱们能够看到 flex-shrink 中提到的 content-based minimum size 会起到限度作用,步骤 2 中的最大最小束缚也会失效。
步骤六
依据 min/max-width(min/max-height) 或者 content-based minimum size 修改 flex item 的尺寸。
咱们把 div#second
的 width
设置为 1000px,而后套用下面的公式:
div#first
: (1059.93 – 200) * 59.93 / (59.93 + 1000) = 48.62pxdiv#second
: (1059.93 – 200) * 1000 / (59.93 + 1000) = 811.31px
如果没有把这一步骤写进去,兴许你会认为 div#first
的最终尺寸就是 59.93 – 48.62,div#second
是 1000 – 811.31。咱们能够在 Firefox 审查一下:
<img width=”350″ alt=” 审查后果 ” src=”https://user-images.githubusercontent.com/13151172/91581319-e37e1c80-e980-11ea-9027-bb0cedd7419c.png”>
div#first
的内容尺寸以及弹性确实和咱们料想的一样,而最终尺寸却是 31.97px。在这里就是 content-based minimum size 失效了,它应用 div#first
的 min-content
作为了它的值。
如果咱们设置了一个确定的 width
,content-based minimum size 其实会取 width
和 min-content
之间的最小值,比方 width: 10px
,那么 div#first
的最终尺寸就是 10px。
__我就是心愿 div#first
的最终尺寸是 59.93 – 48.62 = 11.31px__,这该如何做呢?为 div#first
加上 min-width: 5px
,这样一来它的最终尺寸就成了 11.31px。
如果 flex-grow 失效,如何计算
以这段代码为例:
<div style="display: flex; width: 300px; border: 1px dotted #333;">
<div style="background: #eee; flex-grow: 2;">
short text
</div>
<div style="background: #ccc; flex-grow: 1;">
very looooooooong text
</div>
</div>
- 首先,主轴的可用空间是 300px。
- 每一个 flex item 的根底尺寸是它们的内容尺寸,别离是 59.93px 和 152.82px。
- flex item 的主轴尺寸总和是 212.75px,所以
flex-grow
失效。 - 确定 positive free space 的大小是 87.25px。
- 计算 flex item 调配到的 positive free space,别离是 87.25 2 / (2 + 1) = 58.17px 和 87.25 1 / (2 + 1) = 29.08px。确定最终尺寸别离是 118.1px 和 181.9px。
最初
本文做的总结基本上能够笼罩我目前碰到的业务场景了,将来兴许会碰到更多的场景来调整我对 flexbox 的了解。对于 flex layout 的算法,W3C 有十分具体且简单的介绍,本文做了肯定的调整和简化。如果想要理解更多,能够去 W3C 看看一些介绍。