内容布局(三):Flexbox布局

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

Overview

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

<ul>
  <li><a href="/home">HOME</a></li>
  <li><a href="/onion">Onion</a></li>
  <li><a href="/garlic">Garlic</a></li>
</ul>
Before Flex

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

ul {
  display: flex;
}
After Flex

效果很明显,弹性项<li>全部变成了类似于 inline-block 的元素了——水平布局显现出来了。

Flex 方向:主轴和辅轴

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

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

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

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

align itmes

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

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),或是百分比(相对于主轴而言)。上面我事先写死了弹性项<li>的宽度,这时候所有弹性项的默认 flex-basis 就等价于 4em 了。接着再为首元素单独设置 flex-basis 为 8em:

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

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

flex-basis

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

flex-grow

上图中的容器<ul>还有剩余空间,我们不妨接着试试 flex-grow——为首元素加入伸展系数:

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

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

li:last-child {
  flex-basis: 4em;
  flex-grow: 1;
}
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;
}
flex-wrap

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

小结

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

相关

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

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 204,921评论 6 478
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 87,635评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 151,393评论 0 338
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,836评论 1 277
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,833评论 5 368
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,685评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,043评论 3 399
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,694评论 0 258
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 42,671评论 1 300
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,670评论 2 321
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,779评论 1 332
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,424评论 4 321
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,027评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,984评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,214评论 1 260
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 45,108评论 2 351
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,517评论 2 343