You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
被指定为 auto 时,会取当前 flex item 主轴尺寸属性(width/height)的值,如果取到的值是 auto 时,则会使用 content,也就是根据 flex item 的内容大小来确定。 比如以下结构:
<divstyle="display: flex; width: 200px; border: 1px dotted #333;"><divid="first" style="background: #eee; width: 80px; flex-basis: auto;">
short text
</div><divid="second" style="background: #ccc; flex-basis: auto;">
very looooooooong text
</div></div>
<divstyle="display: flex; width: 200px; border: 1px dotted #333;"><divid="first" style="background: #eee;">
short text
</div><divid="second" style="background: #ccc; width: 280px;">
very looooooooong text
</div></div>
背景
我们经常在某些业务场景中,需要实现子元素均等占用父元素空间的效果。到目前为止,我使用最多的就是用
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
会生效,使 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 的内容大小来确定。 比如以下结构:使用 Firefox(支持
content
,但是min-content
和max-content
是无效值)审查节点的时候可以看到:因为设置了
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 中的定义:
可以用 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
)。如何计算弹性
以上述代码为例:
步骤一
确定主轴(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 应该被分配到多少呢?在这个公式里,可以看到基础尺寸与
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 审查一下: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 生效,如何计算
以这段代码为例:
flex-grow
生效。The text was updated successfully, but these errors were encountered: