深度解析 CSS Flexbox 佈局 - 2020年最新版

峰華發表於2020-04-06

在 CSS flexbox 佈局出現以前,如果要控制 HTML 元素的佈局,要用到很多種奇葩的方式。在水平方向上得用float控制左右對齊,稍一不注意,就會有浮動的元素飛來飛去~。在垂直方向上就更是百家爭鳴了:要麼手動計算高度然後算出中心點,要麼用 line-heightheight 的結合,要麼用十之八九不生效的 vertical-align 屬性等等等等。自從 flex-box 出現以後,一切似乎就豁然開朗了,水平垂直各種花式對齊,空間分配由你做主。當然,要用好它,用對它也不是一件容易的事,今天就給你說說 flex-box 佈局,看完之後你也能熟練的運用它!

2 分鐘視訊入門版:2 分鐘掌握 CSS Grid 佈局

真實 HTML 程式碼(非圖片)示例版:請點此訪問(示例都是真實的 HTML 程式碼,可以使用 chrome 開發者工具檢視屬性。

開啟 Flexbox 佈局

假設有下邊這麼一個 html 結構:

<div class="flex">
  <div class="flex1">Flex 1</div>
  <div class="flex2">Flex 2</div>
  <div class="flex3">Flex 3</div>
</div>
複製程式碼

一個div 容器包含了三個 div 子元素,按照預設的佈局方式進行排列。因為 div 是塊級元素,每個 div 佔了整個一行的空間:

深度解析 CSS Flexbox 佈局 - 2020年最新版

如果要開啟容器的 flex 佈局,只需要在 css 裡邊給 .flex 設定 display: flex 屬性,同時為了演示效果,我給它加上了 100px 的高度:

display: flex;
height: 100px;
複製程式碼

深度解析 CSS Flexbox 佈局 - 2020年最新版

可以看到裡邊的三個元素自動變成了一行,因為 flex 預設是按行進行排列的。Flexbox 佈局是一維佈局方式,要麼按行排列,要麼按列排列。

對齊方式

Flex 佈局有一個隱式的座標空間,水平方向有一條主軸(main-axis),垂直方向上有一條交叉軸(cross-axis):

深度解析 CSS Flexbox 佈局 - 2020年最新版

justify-content

控制主軸(即水平方向)對齊方式使用justify-content屬性,它有下邊幾種對齊方式:

flex-start

flex-start 是預設值,如果是從左到右的文字閱讀習慣(LTR),就是靠左對齊。因為預設的對齊方式,所以跟上邊的例子沒有什麼區別:

justify-content: flex-start;
複製程式碼

深度解析 CSS Flexbox 佈局 - 2020年最新版

center

居中對齊,此時整個 flex 容器被居中到了頁面中間:

深度解析 CSS Flexbox 佈局 - 2020年最新版

flex-end

靠右對齊:

深度解析 CSS Flexbox 佈局 - 2020年最新版

space-between

兩端對齊,這種對齊方式是第一個和最後一個元素貼邊,中間的元素平分剩餘的空間:

深度解析 CSS Flexbox 佈局 - 2020年最新版

space-evenly

分散對齊,所有的元素都平分空間:

深度解析 CSS Flexbox 佈局 - 2020年最新版

space-around

space-evenly類似,但是左右兩邊的留白為平分空間的 1/2.

深度解析 CSS Flexbox 佈局 - 2020年最新版

align-items

控制交叉軸方向(即垂直方向)上的對齊方式使用align-items屬性,有下邊幾種對齊方式:

stretch

stretchalign-items 的預設值,它會自動把子元素拉伸成容器的高度,所以之前的例子裡子元素在垂直方向上都佔滿了容器,只要改變容器的align-items的值,它就會變成內容的高度。stretch 對齊效果如下:

深度解析 CSS Flexbox 佈局 - 2020年最新版

flex-start

靠上對齊,在交叉軸開始的最上方,可以看到子元素不再佔滿容器寬度:

深度解析 CSS Flexbox 佈局 - 2020年最新版

center

居中對齊:

深度解析 CSS Flexbox 佈局 - 2020年最新版

flex-end

靠下對齊:

深度解析 CSS Flexbox 佈局 - 2020年最新版

baseline

基線對齊,如果子元素文字尺寸和行高不同,則子元素會按照文字的基線進行對齊:

.flex2 {
  font-size: 24px;
}
複製程式碼

深度解析 CSS Flexbox 佈局 - 2020年最新版

如果是 flex-start 對齊方式:

深度解析 CSS Flexbox 佈局 - 2020年最新版

align-content

本小節在下邊講到折行時再介紹

子元素覆蓋對齊方式

子元素可以通過設定 align-self 來控制自己在交叉軸上的對齊方式,例如把 .flex3 子元素在垂直方向上靠下對齊:

.flex {
  display: flex;
  align-items: flex-start;
}

.flex3 {
  align-self: flex-end;
}
複製程式碼

深度解析 CSS Flexbox 佈局 - 2020年最新版

在水平方向上控制子元素對齊並沒有justify-self屬性,而是使用margin屬性,通過把左或右邊距設定為auto來控制水平對齊,比如把 flex3 放到最右邊:

.flex3 {
  margin-left: auto;
}
複製程式碼

深度解析 CSS Flexbox 佈局 - 2020年最新版

排列方式

flex 支援按行排布,也支援按列排布。按列排布時,主軸和交叉軸換了方向,但是 align-items 和 justify-content 控制的軸線不變,即 align-items 還是控制交叉軸,justify-content 控制主軸:

深度解析 CSS Flexbox 佈局 - 2020年最新版

所以說,在水平方向上對齊變成了使用align-items,垂直方向則用justify-content

要使 flex 按列排布,只需要設定:

flex-direction: column;
複製程式碼

來看幾個例子:

水平居中對齊

.flex {
  display: flex;
  flex-direction: column;
  align-items: center;
}
複製程式碼

深度解析 CSS Flexbox 佈局 - 2020年最新版

垂直居中對齊

.flex {
  display: flex;
  flex-direction: column;
  justify-content: center;
}
複製程式碼

深度解析 CSS Flexbox 佈局 - 2020年最新版

另外 flex 佈局也可以支援反向按行和列布局,相當於按容器中心線進行 180 度翻轉:

row-reverse

.flex {
  display: flex;
  flex-direction: row-reverse;
}
複製程式碼

深度解析 CSS Flexbox 佈局 - 2020年最新版

column-reverse

列模式下會垂直翻轉:

.flex {
  display: flex;
  flex-direction: column-reverse;
}
複製程式碼

深度解析 CSS Flexbox 佈局 - 2020年最新版

空間佔比

子元素可以通過設定flex屬性來調整空間的佔比,例如讓 flex2 在水平方向上佔據其他子元素的 2 倍大小,可以設定:

.flex1,
.flex3 {
  flex: 1;
}
.flex2 {
  flex: 2;
}
複製程式碼

深度解析 CSS Flexbox 佈局 - 2020年最新版

Flex-basis

在介紹 flex-basis 之前,先講一個概念 main size,即主軸方向的尺寸,那麼,在行排布模式下,也就是水平方向的尺寸,其實就是子元素的寬度,而在列模式下,它是子元素的高度,相對應的也有cross size,即行模式下是子元素的高度,列模式下是寬度。 而flex-basis是用來設定main size的,它的優先順序會高於width它的預設值是auto,即在行模式下,如果子元素設定了寬度,它就取自這個寬度值,沒有設定的話,就是內容的寬度。使用 flex-basis,可以同時管理行模式下的寬度和列模式下的高度。

來看一個例子,把之前的子元素改成固定寬度,比如 200px

.flex > * {
  flex-basis: 200px;
}
複製程式碼

這樣每個子元素寬度變為了 200px:

深度解析 CSS Flexbox 佈局 - 2020年最新版

如果再新增 width 屬性,發現並不會生效:

.flex > * {
  flex-basis: 200px;
  width: 250px;
}
複製程式碼

深度解析 CSS Flexbox 佈局 - 2020年最新版
但是,可以通過設定 min-width來強制設定最小寬度:

.flex > * {
  flex-basis: 200px;
  min-width: 250px;
}
複製程式碼

深度解析 CSS Flexbox 佈局 - 2020年最新版

同理的,在列模式下,flex-basis變成了高度,因為容器高度為 100px,這裡把子元素高度設定成了 30px 總計 90px 來效果:

.flex {
  flex-direction: column;
}

.flex > * {
  flex-basis: 30px;
}
複製程式碼

深度解析 CSS Flexbox 佈局 - 2020年最新版

同樣的,也可以用min-height來控制最小高度。

縮放

(後續例子都假設是行模式)之前的小節簡單說了一下 flex 子元素空間的佔比,這裡把縮放單獨拿出來是為了說明:除了調整 flex 子元素的增長之外,也可以調整收縮,以及flex屬性背後的原理(下一小節)。

flex-grow

先看一下增長,flex-grow,這個屬性是說 flex 容器在有剩餘空間的時候,子元素佔據剩餘空間的佔比。例如,給.flex2子元素設定:

.flex2 {
  flex-grow: 1;
}
複製程式碼

其它的元素保持預設的寬度(即內容的寬度,flex-basis 為 auto),那麼 .flex2 就會自動增長並佔據整個剩餘空間:

深度解析 CSS Flexbox 佈局 - 2020年最新版

如果把三個元素全部設定成 1,那麼所有元素都會自動增長,並各自佔據 1/3 的空間:

深度解析 CSS Flexbox 佈局 - 2020年最新版

使用 flex-grow 就能夠自由的調整元素的空間佔比了,非常適合一些浮動的佈局。

flex-shrink

子元素的收縮是說:當它們的寬度超過 flex 容器之後,該如何進行收縮。通過 flex-shrink 來設定一個數值,數值越大,收縮程度也越大,比如flex-shrink: 2的元素會比flex-shrink:1收縮的值大 2 倍:

.flex1,
.flex3 {
  flex-basis: 600px;
  flex-shrink: 1;
}
.flex2 {
  flex-basis: 600px;
  flex-shrink: 2;
}
複製程式碼

這裡為了方便演示,我把所有的 flex 子元素的 main size (寬度) 都設定成了 600px。在我的顯示器下,flex 容器的寬度是 728px,三個子元素總和 1800px,顯然超出了容器的寬度,那麼根據上邊定義的收縮規則,.flex2 將收縮 2 倍於 .flex.flex3 收縮的空間。下邊的例子中,.flex1.flex3 的寬度變成了 332px,相比於 600px 收縮了 268px,那麼 .flex2 就要收縮 536px (268px * 2) 的寬度,那麼它最後就會剩下 64px (600px - 536px) 的寬度:

深度解析 CSS Flexbox 佈局 - 2020年最新版

再說 flex 屬性

說完flex-growflex-shrinkflex-basis 之後,再來看一下這個 flex 屬性,它其實是前邊三個屬性的縮寫,預設值是 0 1 auto,即不增長,但收縮,收縮比例為 1,flex-basis 為 auto,即取自使用者定義的寬度或內容的寬度。

flex 的值可以是下邊幾種:

  • 指定一個數字 - 例如flex: 1,就等同於是flex: 1 1 0,即自動縮放,比例為 1,flex-basis 為 0。
  • auto - 等同於flex: 1 1 auto
  • 指定兩個數字 - 第一個為flex-grow,第二個,如果是數字則認為是 flex-shrink,如果是寬度,則是flex-basis
  • 指定三個值 - 分別為flex-growflex-shrinkflex-basis

所以說,通過flex屬性可以方便的同時設定flex-growflex-shrinkflex-basis 這三個值。

折行

如果子元素有固定寬度,並且超出了容器的寬度,還不允許收縮的話,那麼可以使用flex-wrap屬性來讓元素進行折行排列,使得每行的元素都不超過容器的寬度。這裡跟 css grid 佈局的主要區別是,它無法控制單獨控制行、列的佔比,比如跨行、誇列,也不能自由定位元素到特定的位置。下邊的示例新增了 2 個元素,一共 5 個,每個元素的 main size 為 300px,然後超出寬度後折行:

.flex {
  flex-wrap: wrap;
}

.flex > * {
  flex-shrink: 0;
  flex-basis: 300px;
}
複製程式碼

深度解析 CSS Flexbox 佈局 - 2020年最新版

align-content

如果 flex 容器開啟了折行,那麼兩行及以上的內容可以通過align-content屬性來控制各行之間在交叉軸上的排列規則,它的取值和 justify-content基本相同,這裡演示其中幾個,還是使用之前三個元素的 flex 容器,每個容器寬度為 300px,超出後換行:

.flex {
  display: flex;
  flex-wrap: wrap;
}
.flex > * {
  flex-basis: 300px;
}
複製程式碼

center

垂直居中:

深度解析 CSS Flexbox 佈局 - 2020年最新版

space-between

兩端對齊:

深度解析 CSS Flexbox 佈局 - 2020年最新版

巢狀的 flex 容器的問題

如果 HTML 結構複雜,有巢狀的 flex 容器,很有可能會遇到巢狀的 flex 容器並不能自動收縮的問題,即使設定了flex-shrink。比如有下邊一個 html 結構:

<div class="flex">
  <div class="flex1">Flex 1</div>
  <div class="flex2">Flex 2</div>
  <div class="flex3">Flex 3</div>
  <div class="flex4">
    <p>
      這是一段很長很長很長很長很長很長很長很長很長很長很長很長長很長很長很長很長很長長很長很長很長很長很長的文字
    </p>
  </div>
</div>
複製程式碼

這裡給之前的 flex 容器新增了一個新的子元素.flex4,這 4 個子元素都設定成flex: 1來平分空間,但是 .flex4 自己本身也是一個flex佈局的容器,裡邊有一長串文字,我想讓它超長之後自動顯示省略號,它的 CSS 程式碼:

.flex {
  display: flex;
}
.flex > * {
  flex: 1;
}
.flex4 {
  display: flex;
  flex: 1;
}
.flex4 > p {
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}
複製程式碼

深度解析 CSS Flexbox 佈局 - 2020年最新版

可以看到,最後本應該佔 1/4 空間的.flex4,因為文字不能換行,直接把 flex 容器撐開了,並且把其他的三個子元素擠成了最小空間,它本應該把文字截短並顯示省略號,這是為什麼呢?原來,flex 容器的 min-width 屬性值為 auto,是由瀏覽器自行計算的,在這裡它取了<p>元素的寬度,使得寬度成為了一整行 <p> 的寬度。那麼要解決這個問題,可以把.flex4 這個巢狀 flex 容器的 min-width 改為0,即最小寬度是0,那麼就可以正常收縮了:

.flex4 {
  display: flex;
  flex: 1;
  min-width: 0;
}
複製程式碼

深度解析 CSS Flexbox 佈局 - 2020年最新版

總結

到這裡,整個 flex 佈局就介紹完了,還是有不少東西的,但不難。相信通過例項你一定可以掌握它的用法,下邊總結一下要點:

  • 開啟 flex 佈局使用display: flex屬性。
  • flex 佈局有主軸和交叉軸,分別使用justify-contentalign-items控制對齊方式。
  • 支援按行或列進行排列,使用flex-direction,另外也支援row-reversecolumn-reverse反向排列。
  • 子元素可以通過flex簡寫形式,或者flex-growflex-shrinkflex-basis 來調整元素的空間佔比和縮放。
  • 通過flex-wrap可以設定 flex 子元素折行顯示。
  • 巢狀flex容器的縮放問題。

你學會了嗎?如果有問題,請留下評論吧。

相關文章