为什么需要理解 FlexBox 的布局算法
如果阅读过一些 Flex 入门的文章,会发现 Flex 的 Container 和 Item 的属性加起来大概十几个,虽然有些多,但是只要花点时间,其实掌握起来还是轻松加愉快的。不过当你高兴地想用 Flex 大显身手,处理复杂多变的布局时,就可能会遇到像:最大/最小尺寸限制等边界问题。这些在大部分 Flex 的介绍文档里都没有太多笔墨描述。我们来看一下例子。
不完美的栅格布局(Grid System)
相信用过 bootstrap 或者 antd 等组件库的同学,都知道有一个布局工具叫栅格。栅格系统的目的是让设计遵循一定的规则,这样可以让设计到实现完全工业化(提高效率),也较大程度保证设计质量。相较于 float 的实现,flex 的栅格实现会简单很多!
栅格布局中的一个很重要特性,是等宽布局,这个在线的例子,用 Flex 可以非常轻松的实现。 这例子,会得到一个很完美的等宽布局,嗯很开心😄。
但是手一抖,就坏了😭。
这是为什么呢?该怎么解决它?
先给解决方案:
- overflow-x: hidden | scroll | auto;
- min-width: 0px; 比较小的值都可以。
- width: 0px; 比较小的值都可以。
overflow-x 解决方案准确的文档定义,没有找到,但是测试了一下,相当于设置了 min-width: 0
,如果你知道官方文档如何定义欢迎留言交流。
不过剩下的其他解决方案是什么鬼,还能 width 和 min-width 为 0?我们带着这个问题来学习一下 FlexBox 算法相关的概念。
弹性布局相关概念
main size 和 cross size
上图中,其实漏掉了挺多信息,main size 可以理解为 main axis 上的尺寸,根据 flex-direction 的设置,可以是 width 和 height。对应的 min/max main size 分别对应 min/max width 和 min/max height。 cross size 正好和 main size 垂直,根据 flex-direction 设置,也可以取 width 或者 height。
automatic minimum size of flex items
min-width 的默认值,在 CSS2 标准的布局类型(block/inline 等)中是 0。但是在 flex 布局中,min-width 的默认值是最小 content 大小(min-content size)。划重点,正是这个定义,导致了上文中 Grid System 被内容溢出撑大。当没有明确设置 width 或者 min-width 的值时,flex-basis 的设置不会起作用,元素大小最后会受到 min-width 值的限制。
flex-basis 属性
the initial main size of the flex item, before free space is distributed according to the flex factors.
flex-basis 定义了 flex item 进行伸缩计算的基准值,以下是 flex-basis 的定义。
- Name: flex-basis
- Value: content | <‘width’>
- Initial: auto
- Applies to: flex items
- Inherited: no
- Percentages: relative to the flex container’s inner main size
- Computed value: specified keyword or a computed value
- Canonical order: per grammar
- Animation type: by computed value type
除了auto | content | <'width'>
,还可以接受公共的一些值。
/* Specify <'width'> */
flex-basis: 10em;
flex-basis: 3px;
flex-basis: auto;
/* Intrinsic sizing keywords */
/* CSS3 Sizing 规范定义的通用属性,目前兼容性不佳,不推荐使用 */
flex-basis: fill;
flex-basis: max-content;
flex-basis: min-content;
flex-basis: fit-content;
/* Automatically size based on the flex item’s content */
flex-basis: content;
/* Global values */
flex-basis: inherit;
flex-basis: initial;
flex-basis: unset;
这里需要特别理解一下 auto 和 content 的取值。 当设置 auto 时,会使用 width 或 height 的取值,如果 width 或 height 的取值是 auto,那么会当作 content 的取值来处理。 当设置 content 时,会按照 item 正常渲染出的 content width 或者 height 取值。
flex base size
flex base size 由 flex-basis 属性值计算而来,会直接用于计算压缩和拉伸的结果。
- 如果 flex-basis 属性设置了明确的值(比如:flex-basis: auto <取 width/height 值>,flex-basis: 10px),flex base size 则等于 flex-basis 的值;
- 如果没有明确的值,计算规则会略微复杂,可以简单理解为根据 item 正常渲染的 width/height 来定,具体细节可以阅读标准文档。
hypothetical main size
中文直译为「假设的 main size」,表示在进行伸缩前,假设的 item 大小。该值等于 flex base size 被 min/max main size 限制的值,用于计算剩余空间。下面是伪代码。
hypothetical_main_size:
if flex_base_size > max_main_size:
return max_main_size
if flex_base_size < min_main_size:
return min_main_size
return flex_base_size
理解伸缩算法
第一步、确定不伸缩的 item
计算 free space 的时候,先确定不伸缩的 item(frozen item),并且计算元素占用的空间,规则如下:
if flex_shrink == 0 and flex_grow == 0:
return min_main_size
if flex_grow > 0 and flex_base_size > max_main_size:
return max_main_size
if flex_shrink > 0 and flex_grow == 0 and flex_base_size < min_main_size:
return min_main_size
第二步、计算 free space
flex container 首先会把 frozen items 的大小去掉,然后再去掉可伸缩 item 的 flex base size。得到初始的 free space
第三步、循环计算拉伸
main_size = flex_base_size + flex_grow_factor * free_space / SUM(flex_grow_factor of items)
第四步、循环计算压缩
scaled_flex_shrink_factor = flex_base_size * flex_shrink_factor
main_size = flex_base_size - scaled_flex_shrink_factor * free_space / SUM(scaled_flex_shrink_factor of items)
flex_base_size 越大的压缩越多,或者说缩小得越快
第五步、循环检查是否超过 min/max main size
如果超过 min/max main size 那么需要取 min/max main size 值。
Flex 布局例子
三明治布局
这个布局就像一个三明治,两边是内容宽度不可伸缩,中间自适应伸缩。它有很多的应用场景,比如:Modal,Title,Page Frame,Select Option。 在没有 flex 的时代,需要用 inline-block、float 等技术来实现,实现复杂,而且很难完美。尤其在垂直方向的布局,不能很好的自动撑开中间高度。用 flex 实现一切都会变得非常简单。在线例子
三明治布局的衍生
除了三列,其实还有很多 2 列的布局,也可以间接地通过三明治布局来实现。
Reference
- www.w3.org/TR/css-flex…
- developer.mozilla.org/en-US/docs/…
- caniuse.com/#search=min…
- css-tricks.com/snippets/cs…
文章作者:严文序(EE People 团队)
邀请优秀的人一起做有挑战的事儿!字节跳动效率工程团队研发职位招聘,想成为技术大牛的伙伴快点进来看!职位介绍