马蹄疾 | 聊聊你可能并无彻底掌握的 Flex 布局

不少谈 Flex 布局的文章都是一个一个属性的干聊,有点像约会时的查户口,枯燥而无趣。css

若是让你硬记中国 34 个省级行政单位,你不只会记的很吃力,可能还无法分清省和省之间的相邻关系。而一旦你心中有了一副中国地图,这些知识就变成信手拈来的东西了。html

这篇文章立志作 Flex 布局的中国地图。git

本文是『horseshoe·Flex专题』系列文章之一,后续会有更多专题推出github

GitHub地址(持续更新):horseshoe布局

博客地址(文章排版真的很漂亮):matiji.cnflex

若是以为对你有帮助,欢迎来 GitHub 点 Star 或者来个人博客亲口告诉我ui

Flexbox叫弹性盒模型,它的使用场景主要是屏幕自适应布局和取代浮动布局。flexbox

细节性的知识须要大量实践,系统性的知识则须要真正理解系统。我认为Flexbox就属于系统性的知识。因此这篇文章从概念入手,力求作到只要阅读一遍,就可让开发者心中有乾坤。spa

一维布局模型

你他妈多是三体看多了吧,啥叫一维布局模型?翻译

一维布局模型,简单讲就是,在主轴方向肯定的状况下,只有,没有

咱们熟悉的二维布局模型有哪些呢?有display: tabledisplay: grid。而咱们今天要讲的display: flex就是妥妥的一维布局模型了。

直接看图。

illustration

能够看到,Flexbox有的概念,却没有的概念。这致使它的表达能力是受限的。

概念

咱们如何肯定一条短线是中文的仍是阿拉伯数字的1呢?

你可能以为我在搞笑。一眼看过去,横向的不就是中文的,纵向的不就是阿拉伯数字的1么。

是的。那是由于咱们遵循着一些默认的约定:两眼连成的线肯定了水平方向。

Flexbox也是这个道理。它是一个一维布局模型,咱们就要找到肯定仅有的维度的那根线。

这就引出了Flexbox的第一个概念:主轴(mian axis)与交叉轴(cross axis)。主轴就是那根仅有的维度线。两眼连成的线与主轴方向保持平行,这就是肉眼看待Flexbox的正确方式。不然有时候它会显得很别扭。

而后咱们再来讲弹性盒子。既然它是一个盒子,那确定得有容器,也得有内容。

也就引出了Flexbox的第二个概念:弹性容器(flex container)与弹性项目(flex item)。

你能够将弹性容器理解为冷战时期的柏林,各方国际政治势力天然就是若干弹性项目了。几个国际大流氓聚在一块儿开会讨论什么呢?固然是开会讨论如何瓜分柏林咯。是的,弹性盒模型讨论的就是弹性项目如何瓜分弹性容器这一舒适的话题。

上一小节咱们提到过,Flexbox是一维布局模型。它带来的结果就是Flexbox有的概念,却没有的概念。

因此引出Flexbox的第三个概念:行(line)。

直接看图。

illustration

最小长度

若是弹性容器有富余空间,那好说,你们分就是了。而若是弹性容器空间不够,弹性项目不只没得分,你们还得挤一挤。那么问题就来了,挤不是无限制的挤,我们就来探讨一下,挤到什么程度,是可忍孰不可忍?

举个例子。

.container {
    display: flex;
    width: 850px;
    padding: 5px;
}
.container .item {
    width: 200px;
    height: 50px;
    margin: 5px;
}
.container .item:nth-child(2) {
    width: 1000px;
}
复制代码

illustration

两个弹性项目的长度加起来的和已经超过了弹性容器,因此不得不挤压。挤压的比率我们先不考虑,我们先观察挤压的方式。有没有发现红色部分都有不一样程度的收缩,可是黄色部分却岿然不动?

黄色部分是什么?是弹性容器的padding和弹性项目的margin。盒子模型我们都了解吧,除了paddingmarginborder以外,是否是只剩content了?

我们能够大胆猜想,Flexbox只敢欺负盒子模型的content,其他都是大爷,惹不起。

别急,再来验证一下。

.container {
    display: flex;
    width: 850px;
    padding: 5px;
}
.container .item {
    width: 200px;
    height: 50px;
    margin: 5px;
}
.container .item:nth-child(2) {
    width: 1000px;
    padding: 0 450px;
}
复制代码

illustration

这下是否是很清楚了?第一个弹性项目的content长度已经变成了0;第二个也好不到哪去,由于盒子长度都被padding占据,它的content长度实际上也是0。都把人家挤破产了,却丝绝不敢动其余的属性,势利眼无疑了。

咱们再来看一个有意思的例子。

.container {
    display: flex;
    width: 850px;
    padding: 5px;
}
.container .item {
    width: 200px;
    height: 50px;
    margin: 5px;
}
.container .item:nth-child(2) {
    width: 1000px;
    padding: 0 300px;
}
.container .item .inner {
    width: 500px;
    height: 100%;
}
复制代码

illustration

我在第二个弹性项目中放了一个长500px的元素,结果你猜怎么着,弹性项目的padding居然有一部分和元素重合了。连子元素都未能幸免。

通常来讲盒子模型的content都是被文字撑开的,咱们最后再来看看文字的状况。

illustration

在弹性项目显式设置了宽度的状况下,弹性项目并不能彻底包裹文字。也就是说文字也帮不了它,既然它声明了宽度,文字撑开的长度最多不能超过显式声明的宽度,超出的文字只能溢出。

illustration

而没有显式声明宽度的状况,文字的宽度就是弹性项目盒子模型的content,Flexbox也拿它没办法。

总结一下:当富余空间不够时,Flexbox只会挤压弹性项目的content,其他如paddingmarginborder彻底不受影响。同时弹性项目没有显式声明宽度的状况下,Flexbox也不会挤压文字。

display

从这里开始,咱们就要讲具体的CSS属性了。

display有两个和Flexbox相关的属性,分别是display: flexdisplay: inline-flex

对于容器内部的项目来讲,效果是同样的。它们的区别在于容器自身应该以块元素仍是行内元素的身份立命。包括table也有这样的区分,就很少讲了。

flex-direction

这个属性声明的是主轴的方位和方向。

首先,主轴可不必定是水平的,主轴切换了那可就什么都变了。

其次,主轴声明的信息有两个,分别是:

  • 它是水平的仍是垂直的?
  • 它在水平或垂直维度上是从左到右仍是从右到左?

因此这里也涉及到四个属性值。

.container {
    flex-direction: row(default) | row-reverse | column | column-reverse;
}
复制代码

illustration

flex-wrap

这个属性声明的是当容器中的项目一行放不下的时候,是让你们挤一挤呢,仍是换行。

其实这里也包含两个信息:

  • 要不要换行?
  • 若是须要换行就会造成多行,多行是从上到下排列仍是从下到上排列?
.container {
    flex-wrap: nowrap(default) | wrap | wrap-reverse;
}
复制代码

illustration

若是把flex-directionflex-wrap结合起来,你们会不会懵逼?上上下下左左右右。其实无论它怎么reverse,flex-direction反转的是主轴的方向,flex-wrap反转的交叉轴的方向。

抓住一些概念性的东西,就不会懵逼了。

flex-flow

这是一个集合属性,能够同时定义flex-directionflex-wrap

也就是说,这一个属性能够一站式声明主轴和交叉轴的特性。

.container {
    flex-flow: row-reverse wrap-reverse;
}
复制代码

justify-content

这个属性声明每行内的项目如何水平对齐。

把弹性容器一行内的项目想象成一行行内元素,justify-contenttext-align的食用方式是同样的。

.container {
    justify-content: flex-start(default) | flex-end | center | space-between | space-around | space-evenly;
}
复制代码

illustration

flex-startflex-endcenter很是表意,我们按下不表。

解释一下后面三个属性值:

  • space-between表示两头的项目对齐容器壁,项目与项目之间的空隙平均分配。所谓的between指的就是项目之间。
  • space-around表示两头的项目与容器壁保留一个单位的空隙,项目与项目之间保持两个单位的空隙。around翻译成中文是周围,指的就是每一个项目左右两边的空隙平均分配。
  • space-evenly表示两头的项目与容器壁之间的空隙和项目与项目之间的空隙都保持一个单位。evenly翻译成中文是均匀,指的是全部空隙平均分配。

align-items

这个属性声明每行内的项目如何垂直对齐。

.container {
    align-items: stretch(default) | flex-start | flex-end | center | baseline;
}
复制代码

illustration

这里有一个问题,若是弹性项目显式的声明了高度,那stretch将再也不起做用。因此这里的例子,我往项目中加了一个子元素,把高度显式的声明在子元素上,这样项目的高度就是被撑开的。

<div class="container">
    <div class="item"><div class="inner">1</div></div>
    <div class="item"><div class="inner">2</div></div>
    <div class="item"><div class="inner">3</div></div>
</div>
复制代码

能够看到,默认状况下,行内的弹性项目会拉伸,至关于声明了高度100%。一旦项目显式的声明了高度,拉伸就再也不起做用了。

align-content

这个属性将容器的一行视为最小单位。它声明的是若是容器的交叉轴方向有富余空间,每行应该如何垂直对齐。

.container {
    align-content: stretch(default) | flex-start | flex-end | center | space-between | space-around | space-evenly;
}
复制代码

illustration

这里也同样,若是弹性项目显式的声明了高度,那stretch将再也不起做用。

同时有两点须要注意:

  • 弹性容器须要显式的声明高度,并且高度必须高于全部行的高度和,不然去哪来的富余空间呢?
  • 若是容器内只有一行,也就是说不须要换行或者不容许换行,那align-content属性将没法生效,由于它是做用于行的,只有一行也敢叫老子跑一趟?

为何没有justify-items

若是你同时了解display: flexdisplay: grid,也许你会发现它们都有xx-itemsxx-content属性。可是别急,再进一步深究,发现Flexbox少了一个justify-items

咱们先来阐述一下xx-itemsxx-content做用范围有什么区别。

  • xx-items做用于项目,这意思很明朗。
  • xx-content又是做用于什么呢?确定是比项目更大的单位,又联想Flexbox的align-content是做用于行,咱们能够大胆猜想xx-content做用于行或者列。

点破到这里,你们应该有点眉目了吧?Flexbox是一维布局模型,它根本没有列的概念。

你说不对呀,既然Flexbox没有列的概念,那不是应该没有justify-content属性,而应该有justify-items属性么?

话是这么说,我当初也是这么认为的。

但后来仔细想想,Flexbox的align-items声明的是一行内的项目如何垂直对齐,与之相对,justify-items声明的就应该是一列内的项目如何水平对齐。好像更离谱。而若是将弹性容器一行内的每一个项目都当作一列,justify-content彷佛就说的通了。

这就比如讨论螃蟹的螯究竟是不是手同样,怎么都以为别扭。只能找一个相对更合理的说法了。

提出这个问题没别的意思,只是想加深你们对Flexbox的理解。

order

从这里开始,涉及到的属性都是弹性项目自身的属性。在大的格局肯定的状况下,项目之间也是能够有一些腾挪空间的。

这个属性声明的是弹性项目自身的次序。只要显式声明了不是默认值0的整数,项目显示的次序将会不一样于源代码定义的次序。

这个主要是留给JavaScript动态控制项目的次序用的,非动态直接修改源代码的次序就行了。

.item {
    order: <integer>; /* default is 0 */
}
复制代码

illustration

flex-grow

这个属性声明的是弹性项目是否要瓜分行内的富余空间,以及如何瓜分。

属性值只容许正整数。

.item {
    flex-grow: <number>; /* default is 0 */
}
复制代码

illustration

首先解释一下什么是富余空间:它是在弹性容器规则和弹性项目显式宽度或者内容的共同约束下,行内剩余的水平空间。好比图例中除去marginpadding的黄色部分。

行内的富余空间是如何被瓜分的呢?

首先,弹性项目要提出申请。好比第一个项目提出flex-grow: 1,意思是说它想要一份富余空间。至于一份富余空间是多少像素,目前还不能肯定。

等全部项目都申请完毕,计算申请的总份数。已知富余空间长度和申请总份数,就能知道一份富余空间是多少像素。

最后根据每人提出申请的份数,分配富余空间。

只有当行内有富余空间时,flex-grow属性才会生效。行内空间已经预先被瓜分完甚至不够时,该属性就管不了了。

flex-shrink

这个属性声明的是弹性项目是否要瓜分行内的负债空间,以及如何瓜分。

属性值只容许正整数。

.item {
    flex-shrink: <number>; /* default is 1 */
}
复制代码

illustration

一样,解释一下什么是负债空间:它是在弹性容器规则和弹性项目显式宽度或者内容的共同约束下,行内短缺的水平空间。此时若是不换行的话,就要求挤压弹性项目的长度。

行内的负债空间的瓜分规则与富余空间的瓜分规则大体相同。

首先,弹性项目要提出申请。只不过这时的份额就再也不是我要多少,而是我还多少。

等全部项目都申请完毕,计算申请的总份数。

这里的问题在于,计算负债空间的长度稍微比较复杂。咱们在文章一开始探讨过弹性项目最小长度的话题,这里再次总结一下:

  • 弹性容器只会挤压弹性项目的content,其他如paddingmarginborder不受影响。
  • 若是弹性项目内有文本或者固定宽度的子元素,这又分两种状况。第一种是项目自己没有显式声明宽度,则最小长度以子元素的长度为准;第二种是项目自己显式声明了宽度,则最小长度以子元素的长度为准,但不能超过项目自己的宽度。结合上面图例中最后两个例子,更容易理解(我知道你已经懵逼了)。

最后根据每人提出申请的份数,分配负债空间。就是还债。

为何flex-grow属性的默认值是0,而flex-shrink属性的默认值是1呢?

由于默认状况下,若是有富余空间我能够不要的,可是有负债空间又没法换行的话,我不得不要。因此flex-shrink属性的默认值是1,意思就是默认状况下,若是空间不够则你们平均的被挤压。

你能够将全部项目的flex-shrink属性值设置为0,如此这般全部项目都铁骨铮铮、不畏强权了。见上面图例的第四个例子。

flex-basis

这个属性声明的是预先分配给弹性项目的长度。它是width属性的替代品,优先级比width高。

.item {
    flex-basis: <length> | auto; /* default is auto */
}
复制代码

illustration

若是widthflex-basis都显式的声明了一个非auto的值,那么flex-basis的优先级更高。不然,哪一个显式声明了就以哪一个为准。

确实不太清楚制定flex-basis属性标准的意义何在。

它们的区别好像仅限于属性值为0的状况。width: 0咱们都知道表示没有宽度,见上面图例的第二个例子;而flex-basis: 0表示之内容的宽度为宽度,见上面图例中的第三个例子。

flex

这是一个集合属性,能够同时定义flex-growflex-shrinkflex-basis

你能够集合三个属性的值,也能够只写flex-grow一个属性的值。

.item {
    flex: <'flex-grow'> | <'flex-grow'> <'flex-shrink'> <'flex-basis'>;
}
复制代码

align-self

这个属性声明的是弹性项目自身在行内的垂直对齐方式。

“我就是我,是颜色不同的烟火”。

.item {
    align-self: auto(default) | stretch | flex-start | flex-end | center | baseline;
}
复制代码

illustration

除了auto以外,align-self的属性值和align-items的属性值是同样的,效果也同样。

align-items: auto是说我默认服从集体,不自搞一套,因此才会多这么个属性值。

为何有align-self属性而没有justify-self属性呢?

这个问题咱们讨论过吧?由于没有jusifty-items属性,因此justify-self属性也无从谈起。

你看人家Grid就有justify-self属性。

其余

有一个小游戏 Flexbox Froggy 能够帮助你轻松的实践Flexbox的各项特性。

本文是『horseshoe·Flex专题』系列文章之一,后续会有更多专题推出

GitHub地址(持续更新):horseshoe

博客地址(文章排版真的很漂亮):matiji.cn

若是以为对你有帮助,欢迎来 GitHub 点 Star 或者来个人博客亲口告诉我

相关文章
相关标签/搜索