理解 Flex 算法和常见应用

3,594 阅读5分钟

为什么需要理解 FlexBox 的布局算法

如果阅读过一些 Flex 入门的文章,会发现 Flex 的 Container 和 Item 的属性加起来大概十几个,虽然有些多,但是只要花点时间,其实掌握起来还是轻松加愉快的。不过当你高兴地想用 Flex 大显身手,处理复杂多变的布局时,就可能会遇到像:最大/最小尺寸限制等边界问题。这些在大部分 Flex 的介绍文档里都没有太多笔墨描述。我们来看一下例子。

不完美的栅格布局(Grid System)

相信用过 bootstrap 或者 antd 等组件库的同学,都知道有一个布局工具叫栅格。栅格系统的目的是让设计遵循一定的规则,这样可以让设计到实现完全工业化(提高效率),也较大程度保证设计质量。相较于 float 的实现,flex 的栅格实现会简单很多!

栅格布局中的一个很重要特性,是等宽布局,这个在线的例子,用 Flex 可以非常轻松的实现。 这例子,会得到一个很完美的等宽布局,嗯很开心😄。

例子1

但是手一抖,就坏了😭。

例子2

这是为什么呢?该怎么解决它?

先给解决方案:

  1. overflow-x: hidden | scroll | auto;
  2. min-width: 0px; 比较小的值都可以。
  3. width: 0px; 比较小的值都可以。

overflow-x 解决方案准确的文档定义,没有找到,但是测试了一下,相当于设置了 min-width: 0,如果你知道官方文档如何定义欢迎留言交流。 不过剩下的其他解决方案是什么鬼,还能 width 和 min-width 为 0?我们带着这个问题来学习一下 FlexBox 算法相关的概念。

弹性布局相关概念

flex concept

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 属性值计算而来,会直接用于计算压缩和拉伸的结果。

  1. 如果 flex-basis 属性设置了明确的值(比如:flex-basis: auto <取 width/height 值>,flex-basis: 10px),flex base size 则等于 flex-basis 的值;
  2. 如果没有明确的值,计算规则会略微复杂,可以简单理解为根据 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 实现一切都会变得非常简单。在线例子

dialog

Header

三明治布局的衍生

除了三列,其实还有很多 2 列的布局,也可以间接地通过三明治布局来实现。

Header

Reference

文章作者:严文序(EE People 团队)

邀请优秀的人一起做有挑战的事儿!字节跳动效率工程团队研发职位招聘,想成为技术大牛的伙伴快点进来看!职位介绍