內(nèi)容布局(三):Flexbox布局

前兩期講了position 布局水平布局,這期接著之前的話題繼續(xù)聊聊更新一點的布局方式——Flexbox(彈性布局)。

Overview

Flexbox 也就是 Flexible Box Layout 模塊,是 CSS 提供的用于布局的一套新語法。這套布局包含針對容器(flex container)和針對其直接子元素(彈性項,flex item)的兩類方法。它可以控制的彈性特征包括:大小、流動方向、橫縱兩軸的排布、順序等等。Flex 已被 IE11 之后的瀏覽器完美支持,除了少數(shù)惡心的應(yīng)用,大家應(yīng)該放心地使用 flexbox;甚至應(yīng)該盡量避免使用傳統(tǒng)的內(nèi)容布局方式。
彈性布局是一個 display 屬性,使用時直接在容器上添加該屬性即可。還是以上一期的導(dǎo)航欄為例:

<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 的元素了——水平布局顯現(xiàn)出來了。

Flex 方向:主軸和輔軸

Flexbox 可以針對某一區(qū)域控制其中元素的順序、大小、分布以及對齊。事實上這個區(qū)域的元素可以沿著兩個方向排列:

  • 主軸(main axis):也就是橫軸,水平方向的排列
  • 輔軸(cross axis):也就是縱軸,垂直方向的排列

默認(rèn)情況下子元素是依據(jù)主軸從左至右排布,也就是flex-direction: row;,但還有:row-reverse、 column、column-reverse等幾種排布。我們對比一下效果:

Flex direction

排布和我們的常識并不沖突,所以理解起來不難,

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

空間與對齊

上面提到了主軸和輔軸,flex 還可以對這兩個方向上的排布作出調(diào)整,這里需要記住兩個英語單詞:justifyalign;從語義來說,前者翻譯過來是(水平)排齊,后者是(垂直)排齊。CSS 關(guān)鍵字里經(jīng)常出現(xiàn)這兩個單詞,大家記得區(qū)分語義。

justify-content

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

ul {
  display: flex;
  justify-content: flex-start;
}
justify-content

默認(rèn)為 flex-start,表示左對齊;其他幾個也可顧名思義:右對齊、水平居中、居間留白、空間環(huán)繞。

這里順便提一個與 flex 有點關(guān)系的常規(guī)的布局:如何讓首元素(HOME)左對齊,剩余元素右對齊呢?

margin auto

很簡單,讓首元素的右 margin 為 auto 即可,原理是:在 flex 布局里,外邊距 auto 會耗盡 flex 容器的所剩空間,剩余彈性項就這樣被前面的 margin 擠到了行尾。

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

同理,讓第二個元素的左 margin 為 auto 也可以實現(xiàn)上面一模一樣的效果:

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

再換一個布局,如何把上圖的首元素擠到右側(cè),剩余元素依舊左對齊呢?

Order

這里需要用到 Flexbox 的 order 屬性,它是彈性項的屬性,可以幫助我們完全擺脫源碼中的順序約數(shù)。Order 的默認(rèn)值是 0,表示按源碼中的順序排列。我給首元素加一個很大的 order 值,如 999;瀏覽器發(fā)現(xiàn)它的 order 大于所有兄弟元素,就直接把它放到了行尾。然后我再把它的左 margin 設(shè)為 auto,首元素就被擠到最右側(cè)了。

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

Order 值不需要連續(xù),且可正可負(fù);只要可以比大小,相應(yīng)的項就可以按值升序排列了。

align-items

Flexbox 有水平對齊,自然也有垂直對齊,叫 align-itmes,是容器屬性,主要有四種: stretch、flex-start、center、flex-end。

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

默認(rèn)值是stretch——高度自動拉伸;其他值不拉伸高度,分別是上中下對齊。上述示例中所有元素的高度是一致的,所以看不出垂直對齊的效果;下圖我特意把首元素的高度增加了一倍,大家就可以發(fā)現(xiàn)區(qū)別了:

align itmes

align-items 屬性會對所有彈性項起效果,假如我只想調(diào)整末尾元素為居中對齊,那該怎么辦呢?

align-self

Flexbox 提供了一個叫 align-self 的屬性為彈性項調(diào)整個別項的對齊方式。我們只需要把 last-child 設(shè)成垂直居中即可:

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

這里順便提一下,flexbox 并沒有justify-self這個屬性,也就是說我們無法調(diào)整個別項的水平排布。嗯,輔軸功能并不見得比主軸弱。

伸縮

伸縮屬性也是用于調(diào)整個別項的排布,屬性名就叫flex

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

事實上,它是三個屬性的語法糖,按序分別是:

  • flex-grow:用于拉伸的彈性系數(shù)(自然數(shù)),默認(rèn)值是 0
  • flex-shrink:與 flex-grow 相反的彈性系數(shù),應(yīng)用于收縮狀態(tài),默認(rèn)值是 1
  • flex-basis:在上面兩個屬性修正前的基準(zhǔn)大小

伸縮屬性有點難講,為了方便計算,我稍微調(diào)整了一下尺寸,順便把各項的尺寸顯示出來:

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

li {
  width: 4em;
}

flex-basis

flex-basis 的默認(rèn)值是 auto,但也可以是單位值(如 16px、1em),或是百分比(相對于主軸而言)。上面我事先寫死了彈性項<li>的寬度,這時候所有彈性項的默認(rèn) flex-basis 就等價于 4em 了。接著再為首元素單獨設(shè)置 flex-basis 為 8em:

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

我們比對一下設(shè)置前后的效果:

flex-basis

很明顯,首元素寬度變成了之前的兩倍。在未被伸縮屬性修正前,flex-basis 可以直接當(dāng)成寬度來用:若設(shè)置了 width 屬性,默認(rèn)值 auto 等于 width;若沒有設(shè)置 width,auto 又會根據(jù)內(nèi)容實際長度設(shè)置數(shù)值。當(dāng)然,flex-basis 并沒有這么簡單,我們接著往下看。

flex-grow

上圖中的容器<ul>還有剩余空間,我們不妨接著試試 flex-grow——為首元素加入伸展系數(shù):

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

在上述代碼的基礎(chǔ)上,試著給另一個彈性項 last-child 添加 flex-grow:

li:last-child {
  flex-basis: 4em;
  flex-grow: 1;
}
flex-grow 2

嗯,我們應(yīng)該可以看出一些端倪了:

  1. flex-grow 會拉伸彈性項,并最終填滿父元素的所有剩余空間
  2. 若多個彈性項同時設(shè)置了 flex-grow,它們會按一定的比例分配父元素的剩余空間

這里所謂的剩余空間 = 容器總長度 - 所有彈性項的 flex-basis 之和。

修正后,彈性項的實際長度 = 自身 flex-basis + 剩余空間 × 自身的 flex-grow / 所有彈性項的 flex-grow。

公式出來了,我很難再說得更清楚了。

flex-shrink

再看看收縮場景——當(dāng) flex 容器空間小于所有彈性項 flex-basis 之和時,flexbox 又會按一定比例壓縮各彈性項;flex-shrink 就是應(yīng)用于這個場景的收縮比例系數(shù)。

在收縮場景里,總壓縮長度(負(fù)空間) = 所有彈性項的 flex-basis 之和 - 容器總長度。

修正后,彈性項實際長度 = 自身 flex-basis - 總壓縮寬度 × [ 自身(flex-grow × flex-shrink) / 所有彈性項的(flex-grow × flex-shrink) ]。

flex-wrap

上面這個收縮計算有點麻煩了,而且也沒太必要計算出實際寬度。在很多的應(yīng)用場景里,我們會直接給容器加一個flex-wrap: wrap;屬性:當(dāng) flex 容器過窄時,彈性項直接換行即可。

ul {
  display: flex;
  flex-wrap: wrap;
}
flex-wrap

這種根據(jù)寬度自動調(diào)整布局的設(shè)計,被稱為響應(yīng)式設(shè)計,這就是后話了。

小結(jié)

本文簡單介紹了 flexbox 最常用的幾個屬性,涵蓋了橫縱方向的排布、對齊、大小、次序調(diào)整等等方面。但是本文只是入門介紹,flexbox 的語法還有不少,具體實踐更遠(yuǎn)超本文描述,還是希望大家能繼續(xù)研讀 W3C 文檔。

相關(guān)

文章同步發(fā)布于an-Onion 的 Github。碼字不易,歡迎點贊。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

友情鏈接更多精彩內(nèi)容