前言

从第一代 DIV + CSS 正常流的排版方式,到 Flexbox 弹性盒模型,再到 Grid 的排版方式,以及最新的可以利用浏览器渲染引擎的 CSS Houdini。今天我们先来讲一下 Flexbox 弹性盒模型的基础以及如何实现常见布局,并对比传统 DIV + CSS 布局。

caniuse flexbox

容器

想要使用 Flex 布局,我们先要找一个元素将它指定为容器,然后就可以通过不同的容器属性,规定容器内 item 项目(子元素)的水平垂直排列(默认是从上到下,从左到右)、对齐方式等一些较为大力度的规则。容器内的项目,通过项目属性,可以进一步达到更细粒度的控制,后面会讲到。

display: flex/inline-flex;

注意,设为 Flex 布局以后,子元素的 floatclearvertical-align 属性将失效。

容器属性

flex-direction

主轴方向,项目排列方向

  • row - 水平方向,从左到右
  • row-reverse - 水平方向,从右到左
  • column - 垂直方向,从上到下
  • column-reverse - 垂直方向,从下到上
flex-direction: row | row-reverse | column | column-reverse;

flex-wrap

项目超出容器后的换行方式

  • nowrap - 不换行
  • wrap - 换行,下一行从左往右排列
  • wrap-reverse - 换行,下一行从右往左排列
flex-wrap: nowrap | wrap | wrap-reverse;

flex-flow

flex-directionflex-wrap 的组合

flex-flow: <flex-direction> || <flex-wrap>;

justify-content

项目在主轴(横向轴)上的对齐方式,默认从左侧开始排列

  • flex-start - 从左往右
  • flex-end - 从右侧开始,从右往左
  • center - 从左往右,中间对齐
  • space-between - 从左往右,元素间均摊容器剩余空间
  • space-around - 从左往右,元素间均摊容器剩余空间,第一个和最后一个元素两侧都会均摊剩余空间,不会贴边
justify-content: flex-start | flex-end | center | space-between | space-around;

align-items

项目在交叉轴上的对齐方式

  • flex-start
  • flex-end
  • center
  • space-between
  • space-around
align-items: flex-start | flex-end | center | space-between | space-around;

align-content

  • flex-start
  • flex-end
  • center
  • space-between
  • space-around
  • stretch
align-content: flex-start | flex-end | center | space-between | space-around | stretch;

项目属性

order

项目排序,从小到大排列

order: <integer>;

flex-grow

项目增长,占据剩余空间的份数,默认是 0,不占据剩余空间

flex-grow: <number>; /* default 0 */

示例图片:

示例代码:

<style>
  section {
    display: flex;
    background-color: #000;
    color: #fff;
    font-size: 30px;
  }
</style>

<section>
  <div style="flex-grow: 1;background-color: crimson;">1</div>
  <div style="width: 100px;">2</div>
  <div>333</div>
  <div style="flex-grow: 1;background-color: green;">4</div>
</section>

flex-shrink

项目收缩时,占收缩总量的份数,默认是 1,最小收缩至项目内容占据的最小宽度

flex-shrink: <number>; /* default 1 */

示例图片:

上图所示的容器,保持内容完整的最小宽度为:100 + 100 + 54 + 18 = 272

当浏览器窗口宽度缩小 15px 时,设置了 flex-shrink: 2; 的项目 1 的宽度由 100px 减少到了 90px,减少的宽度占本次减少宽度的 2/3

而未设置 flex-shrink 的项目 2,宽度由 100px 减少到了 95px,减少的宽度占本次减少宽度的 1/3

示例代码:

<style>
  section {
    display: flex;
    background-color: #000;
    color: #fff;
    font-size: 30px;
  }
</style>

<section>
  <div style="width: 100px;flex-shrink: 2;background-color: crimson;">1</div>
  <div style="width: 100px;">2</div>
  <div>333</div>
  <div style="background-color: green;">4</div>
</section>

那么问题来了,细心的同学可能发现了,项目 1 和项目 2 的减少占比为什么不是 2/51/5 呢?

答案是:项目 3 和项目 4,没法再减少了。

在没有主动设置项目宽度的情况下,项目宽度由其内容所占据的宽度决定,默认情况下就是最小宽度。

flex-basis

项目的起始宽度,默认值为 auto,表示起始宽度由内容决定

flex-basis: <length> | auto; /* default auto */

flex

flex-growflex-shrinkflex-basis 组合

flex: none | [ <'flex-grow'> <'flex-shrink'>? || <'flex-basis'> ]

align-self

项目自身的对齐方式,默认继承容器 align-items 的值

  • auto
  • flex-start
  • flex-end
  • center
  • baseline
  • stretch
align-self: auto | flex-start | flex-end | center | baseline | stretch;

实现常见布局

水平居中

传统布局:

.parent {
  position: relative;
}
.children {
  position: absolute;
  top: 50%;
  left: 50%;
  transform: translate(-50%, -50%);
}

Flex 布局:

.parent {
  display: flex;
  justify-content: center;
  align-items: center;
}

圣杯布局

传统布局:

.parent {
  min-height: 100vh;
  font-size: 30px;
  text-align: center;
}
.header {
  height: 100px;
  background-color: #efef;
}
.main {
  min-height: calc(100vh - 200px);
}
.main:after {
  display: block;
  content: "";
  clear: both;
}
.left {
  float: left;
  width: 200px;
  min-height: calc(100vh - 200px);
  background-color: red;
}
.center {
  min-height: calc(100vh - 200px);
  margin: 0 200px;
  background-color: green;
}
.right {
  float: right;
  width: 200px;
  min-height: calc(100vh - 200px);
  background-color: blue;
}
.footer {
  height: 100px;
  background-color: #efef;
}
<div class="parent">
  <div class="header">header</div>
  <div class="main">
    <div class="left">left</div>
    <div class="right">right</div>
    <div class="center">center</div>
  </div>
  <div class="footer">footer</div>
</div>

Flex 布局:

.parent {
  display: flex;
  min-height: 100vh;
  flex-direction: column;
  font-size: 30px;
  text-align: center;
}
.header {
  height: 100px;
  background-color: #efef;
}
.main {
  display: flex;
  flex: 1;
}
.left {
  flex-basis: 200px;
  background-color: red;
}
.center {
  flex: 1;
  background-color: green;
}
.right {
  flex-basis: 200px;
  background-color: blue;
}
.footer {
  height: 100px;
  background-color: #efef;
}
<div class="parent">
  <div class="header">header</div>
  <div class="main">
    <div class="left">left</div>
    <div class="center">center</div>
    <div class="right">right</div>
  </div>
  <div class="footer">footer</div>
</div>

相较于传统基于盒模型的布局方案,Flex 布局语义清晰,更为简洁,但是也不是万能的,如果要实现元素嵌套、样式细节比较多场景,还是传统的布局方式更灵活、可读,需要在适合的场景灵活运用,才能事半功倍。

参考链接

  • http://www.ruanyifeng.com/blog/2015/07/flex-grammar.html
  • https://blog.poetries.top/css-reference/flexbox/