馬蹄疾 | 聊聊你可能並無徹底掌握的 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 或者來個人博客親口告訴我

相關文章
相關標籤/搜索