内容布局(三):Flexbox布局

前两期讲了position 布局和水平布局,这期接着之前的话题继续聊聊更新一点的布局方式——Flexbox(弹性布局)。

Overview

Flexbox 也就是 Flexible Box Layout 模块,是 CSS 提供的用于布局的一套新语法。这套布局包含针对容器(flex container)和针对其直接子元素(弹性项,flex item)的两类方法。它可以控制的弹性特征包括:大小、流动方向、横纵两轴的排布、顺序等等。Flex 已被 IE11 之后的浏览器完美支持,除了少数恶心的应用,大家应该放心地使用 flexbox;甚至应该尽量避免使用传统的内容布局方式。
弹性布局是一个 display 属性,使用时直接在容器上添加该属性即可。还是以上一期的导航栏为例:


内容布局(三):Flexbox布局_第1张图片
Before Flex

我们给容器 ul 加上 flex 属性:

ul {
  display: flex;
}
After Flex

效果很明显,弹性项

  • 全部变成了类似于 inline-block 的元素了——水平布局显现出来了。

    Flex 方向:主轴和辅轴

    Flexbox 可以针对某一区域控制其中元素的顺序、大小、分布以及对齐。事实上这个区域的元素可以沿着两个方向排列:

    • 主轴(main axis):也就是横轴,水平方向的排列
    • 辅轴(cross axis):也就是纵轴,垂直方向的排列

    默认情况下子元素是依据主轴从左至右排布,也就是flex-direction: row;,但还有:row-reversecolumncolumn-reverse等几种排布。我们对比一下效果:

    内容布局(三):Flexbox布局_第2张图片
    Flex direction

    排布和我们的常识并不冲突,所以理解起来不难,

    • row:从左往右排列
    • row-reverse:从右往左排列
    • column:从上至下排列
    • column-reverse:从下至上排列

    空间与对齐

    上面提到了主轴和辅轴,flex 还可以对这两个方向上的排布作出调整,这里需要记住两个英语单词:justifyalign;从语义来说,前者翻译过来是(水平)排齐,后者是(垂直)排齐。CSS 关键字里经常出现这两个单词,大家记得区分语义。

    justify-content

    我们先看水平排布,它的属性叫justify-content,主要有五种:flex-start、flex-end、center、space-between、space-around。

    ul {
      display: flex;
      justify-content: flex-start;
    }
    
    内容布局(三):Flexbox布局_第3张图片
    justify-content

    默认为 flex-start,表示左对齐;其他几个也可顾名思义:右对齐、水平居中、居间留白、空间环绕。

    这里顺便提一个与 flex 有点关系的常规的布局:如何让首元素(HOME)左对齐,剩余元素右对齐呢?

    margin auto

    很简单,让首元素的右 margin 为 auto 即可,原理是:在 flex 布局里,外边距 auto 会耗尽 flex 容器的所剩空间,剩余弹性项就这样被前面的 margin 挤到了行尾。

    ul li:first-child {
      margin-right: auto;
    }
    

    同理,让第二个元素的左 margin 为 auto 也可以实现上面一模一样的效果:

    ul li:nth-child(2) {
      margin-left: auto;
    }
    

    再换一个布局,如何把上图的首元素挤到右侧,剩余元素依旧左对齐呢?

    Order

    这里需要用到 Flexbox 的 order 属性,它是弹性项的属性,可以帮助我们完全摆脱源码中的顺序约数。Order 的默认值是 0,表示按源码中的顺序排列。我给首元素加一个很大的 order 值,如 999;浏览器发现它的 order 大于所有兄弟元素,就直接把它放到了行尾。然后我再把它的左 margin 设为 auto,首元素就被挤到最右侧了。

    ul li:first-child {
      order: 999;
      margin-left: auto;
    }
    

    Order 值不需要连续,且可正可负;只要可以比大小,相应的项就可以按值升序排列了。

    align-items

    Flexbox 有水平对齐,自然也有垂直对齐,叫 align-itmes,是容器属性,主要有四种: stretch、flex-start、center、flex-end。

    ul {
      display: flex;
      align-items: stretch;
    }
    

    默认值是stretch——高度自动拉伸;其他值不拉伸高度,分别是上中下对齐。上述示例中所有元素的高度是一致的,所以看不出垂直对齐的效果;下图我特意把首元素的高度增加了一倍,大家就可以发现区别了:

    内容布局(三):Flexbox布局_第4张图片
    align itmes

    align-items 属性会对所有弹性项起效果,假如我只想调整末尾元素为居中对齐,那该怎么办呢?

    内容布局(三):Flexbox布局_第5张图片
    align-self

    Flexbox 提供了一个叫 align-self 的属性为弹性项调整个别项的对齐方式。我们只需要把 last-child 设成垂直居中即可:

    li:last-child {
      align-self: center;
    }
    

    这里顺便提一下,flexbox 并没有justify-self这个属性,也就是说我们无法调整个别项的水平排布。嗯,辅轴功能并不见得比主轴弱。

    伸缩

    伸缩属性也是用于调整个别项的排布,属性名就叫flex

    li:fist-child {
      flex: 1 1 auto;
    }
    

    事实上,它是三个属性的语法糖,按序分别是:

    • flex-grow:用于拉伸的弹性系数(自然数),默认值是 0
    • flex-shrink:与 flex-grow 相反的弹性系数,应用于收缩状态,默认值是 1
    • flex-basis:在上面两个属性修正前的基准大小

    伸缩属性有点难讲,为了方便计算,我稍微调整了一下尺寸,顺便把各项的尺寸显示出来:

    ul {
      display: flex;
      width: 24em;
    }
    
    li {
      width: 4em;
    }
    

    flex-basis

    flex-basis 的默认值是 auto,但也可以是单位值(如 16px、1em),或是百分比(相对于主轴而言)。上面我事先写死了弹性项

  • 的宽度,这时候所有弹性项的默认 flex-basis 就等价于 4em 了。接着再为首元素单独设置 flex-basis 为 8em:

    li:first-child {
      flex-basis: 8em;
    }
    

    我们比对一下设置前后的效果:

    内容布局(三):Flexbox布局_第6张图片
    flex-basis

    很明显,首元素宽度变成了之前的两倍。在未被伸缩属性修正前,flex-basis 可以直接当成宽度来用:若设置了 width 属性,默认值 auto 等于 width;若没有设置 width,auto 又会根据内容实际长度设置数值。当然,flex-basis 并没有这么简单,我们接着往下看。

    flex-grow

    上图中的容器

      还有剩余空间,我们不妨接着试试 flex-grow——为首元素加入伸展系数:

      li:first-child {
        flex-basis: 8em;
        flex-grow: 1;
      }
      
      内容布局(三):Flexbox布局_第7张图片
      flex-grow 1

      在上述代码的基础上,试着给另一个弹性项 last-child 添加 flex-grow:

      li:last-child {
        flex-basis: 4em;
        flex-grow: 1;
      }
      
      内容布局(三):Flexbox布局_第8张图片
      flex-grow 2

      嗯,我们应该可以看出一些端倪了:

      1. flex-grow 会拉伸弹性项,并最终填满父元素的所有剩余空间
      2. 若多个弹性项同时设置了 flex-grow,它们会按一定的比例分配父元素的剩余空间

      这里所谓的剩余空间 = 容器总长度 - 所有弹性项的 flex-basis 之和。

      修正后,弹性项的实际长度 = 自身 flex-basis + 剩余空间 × 自身的 flex-grow / 所有弹性项的 flex-grow。

      公式出来了,我很难再说得更清楚了。

      flex-shrink

      再看看收缩场景——当 flex 容器空间小于所有弹性项 flex-basis 之和时,flexbox 又会按一定比例压缩各弹性项;flex-shrink 就是应用于这个场景的收缩比例系数。

      在收缩场景里,总压缩长度(负空间) = 所有弹性项的 flex-basis 之和 - 容器总长度。

      修正后,弹性项实际长度 = 自身 flex-basis - 总压缩宽度 × [ 自身(flex-grow × flex-shrink) / 所有弹性项的(flex-grow × flex-shrink) ]

      flex-wrap

      上面这个收缩计算有点麻烦了,而且也没太必要计算出实际宽度。在很多的应用场景里,我们会直接给容器加一个flex-wrap: wrap;属性:当 flex 容器过窄时,弹性项直接换行即可。

      ul {
        display: flex;
        flex-wrap: wrap;
      }
      
      内容布局(三):Flexbox布局_第9张图片
      flex-wrap

      这种根据宽度自动调整布局的设计,被称为响应式设计,这就是后话了。

      小结

      本文简单介绍了 flexbox 最常用的几个属性,涵盖了横纵方向的排布、对齐、大小、次序调整等等方面。但是本文只是入门介绍,flexbox 的语法还有不少,具体实践更远超本文描述,还是希望大家能继续研读 W3C 文档。

      相关

      • 《内容布局(一):position 布局》
      • 《内容布局(二):水平布局》

      文章同步发布于an-Onion 的 Github。码字不易,欢迎点赞。

  • 你可能感兴趣的:(内容布局(三):Flexbox布局)