一文搞懂 CSS Flexbox 佈局 - 2020年最新版

在 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 佔了整個一行的空間:

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

display: flex;
height: 100px;

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

對齊方式

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

justify-content

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

flex-start

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

justify-content: flex-start;

center

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

flex-end

靠右對齊:

space-between

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

space-evenly

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

space-around

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

align-items

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

stretch

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

flex-start

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

center

居中對齊:

flex-end

靠下對齊:

baseline

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

.flex2 {
  font-size: 24px;
}

如果是 flex-start 對齊方式:

align-content

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

子元素覆蓋對齊方式

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

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

.flex3 {
  align-self: flex-end;
}

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

.flex3 {
  margin-left: auto;
}

排列方式

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

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

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

flex-direction: column;

來看幾個例子:

水平居中對齊

.flex {
  display: flex;
  flex-direction: column;
  align-items: center;
}

垂直居中對齊

.flex {
  display: flex;
  flex-direction: column;
  justify-content: center;
}

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

row-reverse

.flex {
  display: flex;
  flex-direction: row-reverse;
}

column-reverse

列模式下會垂直翻轉:

.flex {
  display: flex;
  flex-direction: column-reverse;
}

空間佔比

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

.flex1,
.flex3 {
  flex: 1;
}
.flex2 {
  flex: 2;
}

Flex-basis

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

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

.flex > * {
  flex-basis: 200px;
}

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

如果再添加 width 屬性,發現並不會生效:

.flex > * {
  flex-basis: 200px;
  width: 250px;
}


但是,可以通過設置 min-width來強制設置最小寬度:

.flex > * {
  flex-basis: 200px;
  min-width: 250px;
}

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

.flex {
  flex-direction: column;
}

.flex > * {
  flex-basis: 30px;
}

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

縮放

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

flex-grow

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

.flex2 {
  flex-grow: 1;
}

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

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

使用 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) 的寬度:

再說 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;
}

align-content

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

.flex {
  display: flex;
  flex-wrap: wrap;
}
.flex > * {
  flex-basis: 300px;
}

center

垂直居中:

space-between

兩端對齊:

嵌套的 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;
}

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

.flex4 {
  display: flex;
  flex: 1;
  min-width: 0;
}

總結

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

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

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

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章