伸縮自如的 CSS Flexbox

目前工作上切版大部分都是使用 flex 語法了,不過我發現我只對基礎的 flex 語法比較熟,如果要做進階的調整就會卡關了,像是 flex: 1 2 200px 這類的 😭,因此決定寫一篇筆記來幫助自己理解 flex

在學習 flex 之前一定要對 flexbox 模型有一定的認識,下圖就是 flexbox 的結構,主軸(main axis)和交錯軸(Cross Axis)為相對關係,主軸的方向可以做修改,當主軸變了交錯軸也會隨之改變,舉例來說,如果主軸改為垂直方向,那麼交錯軸就會變成水平方向

不管是主軸或是交錯軸都具有方向性,這會決定主軸的起始點和終點


接下來開始實作 flex! 先準備一個父容器 container(200px * 200px ),裡面放了三個子元素 item( 50px * 50px ),然後額外寫一個鄰居與父容器並行,在還沒做任何設定之前會如下圖

display : flex | inline-flex

我們在父容器身上設定 display:flex,就會發現子元素可以水平排列了,假設父容器需要和「我是鄰居」這個 span 並排顯示的話,就需要設置成 display:inline-flex,而 flex 和 inline-flex 的差異就如同 block 和 inline-block,前者不可與其他元素並排,後者則是可以

flex-direction : row | row-reverse | column | column-reverse

假如我們需要修改主軸的方向,可以透過 flex-direction 來修改主軸的方向,row(水平  —  由左至右) 、row-reverse(水平反轉  —  由右至左) 、column(垂直  —  由上至下) 、column-reverse(垂直反轉  —  由下至上),搭配箭頭來看會更清楚主軸的方向性為何

justify-content : flex-start | flex-end | center | space-between | space-around | space-evenly

justify-content 控制子元素的水平對齊,正確來說是主軸對齊,預設值為 flex-start,如上圖最左邊那個容器,子元素均向左對齊,假如我們需要將父容器裡面的子容器置中的話,只要將 justify-content 設定為 center 即可,如果需要子容器等距間隔開來,則可以使用 space-between、 space-evenly、space-around 等等,這三者會有些許的不同,端看使用情境來做選擇

align-items : flex-start | flex-end | center | stretchbaseline

align-items 控制子元素的垂直對齊,預設值為 flex-start,個人最常使用的值為 center 和 flex-end

而 stretch 和 baseline 只會在子元素高度不一致的時候有效果,下圖可以看到左邊兩個父容器分別設定了 stretch 和 baseline,但卻沒有任何的反應,是因為子元素都設定一樣的高度,因此 stretch 和 baseline 的行為就像 flex-start 一樣,都是對齊上方,而右邊兩個父容器刻意將 1 號子元素 height 設定為 80px , 2 號不設定高度,3 號設定 height 為 50px,可以看到 stretch 的效果是將沒有設定高度的子元素稱滿整個父容器,而 baseline 是以子元素的基線做為對齊線

flex-wrap : nowrap | wrap | wrap-reverse

為了演示 flex-wrap 語法,我們多新增三個子元素,就會發現一件詭異的事情,一個子元素的寬為 50px,6 個就是 300px,父容器的寬為 200px,理當來說子元素應該要變兩行才對,怎麼會全部擠在同一行,那是因為 flex-wrap 預設為 nowrap,全部的子元素都會被放在同一行,如果要斷行的話,只要設定 wrap 即可,另外如果要顛倒斷行的順序可以寫 wrap-reverse

align-content : flex-start | flex-end | center | space-between | space-around | space-evenly

align-items 負責單行的交錯軸對齊,而 align-content 負責多行的交錯軸對齊,基本上熟悉 justify-content 的邏輯,align-content 就能快速上手

以上都是針對整體做設定,接下來會介紹針對單一元素做設定

order

針對子元素設定 order 的話,就可以改變排列的順序,不過設定的邏輯跟我預想的不太一樣,將 3 號子元素的 order 設定為 1 結果是排在最後一個,設定為-1 則是排在第一個,假設每個子元素都有設定 order 的話,排列的順序就會是預期的,ex 將 1 號子元素 order 設定為 3,2 號容器設定為 5…

align-self: auto | stretch | flex-start | center | flex-end | baseline

針對單一元素設定交錯軸的對齊,假如父容器已經有設定 align-items 的話,align-self 會沒作用,這點要特別注意

接下來就來到今天的重頭戲,也是我比較不熟的部分  — flex!

flex-grow

設定子元素的延伸,預設值為 0 不縮放,若父容器還有多餘的空間,則子元素會依照設定 flex-grow 的比例做延伸

flex-shrink

設定子元素的縮放,預設值為 1,若設定為 0 則不縮放,當父容器的空間不足,則子元素會依照 flex-shrink 的比例做縮放

flex-basis

子元素的基礎寬度,預設為 auto

flex

flex 為 flex-grow、flex-shirk 和 flex-basis 的縮寫,如果只寫一個值代表設定 flex-grow

搭配實作來看看 flex-grow 和 flex-shirk 是怎麼運作的吧!先將父容器寬度為 400px,藍色的子元素設定為 width: 200px,綠色的子元素設定為 width: 100px ,畫面會如下,是沒被撐滿的,如果要讓子元素填滿父容器可以設定 flex-grow

將藍色子元素設定為 flex-grow:1,綠色子元素為 flex-grow:2,flex-grow 的計算步驟如下:

  1. 先計算父容器剩餘空間:400-(200+100) = 100
  2. 子容器的 flex-grow 分別為 1, 2,剩餘空間除以 3(1+2)
  3. 100 / 3 = 33.33…
  4. 藍色子元素的寬 = 200+(1*33.33) = 233.34
  5. 綠色子元素的寬 = 100+(2*33.33) = 166.65

接下來示範 flex-shirk,父容器的寬度維持 400px,藍色的子元素設定為 width: 300px,綠色的子元素設定為 width: 200px,這時的子元素是寬度總和是大於父容器的,因此我們可以透過 flex-shrink 來依照設定的比例做縮放

將藍色子元素設定為 flex-shrink:2,綠色子元素為 flex-shrink:1,flex-shrink 的計算步驟如下:

  1. 先計算超出的部分:300+200–400 = 100
  2. 計算權重  : 2*300 + 1*200 = 800
  3. 計算藍色子元素縮放了多少:(100*2[flex-shirk]*300[width]) / 800 = 75
  4. 計算綠色子元素縮放了多少:(100*1[flex-shirk]*200[width]) / 800 = 25
  5. 藍色子元素縮放後的寬度為 300–75 = 225
  6. 綠色子元素縮放後的寬度為 200–25=175

想不到 flex-shrink 的計算方式居然跟 flex-grow 的方式不一樣,而且還複雜了許多,不過實務上應該是不會需要精算到這麼仔細啦,有個基本概念就可以了,經過這次實作對 flex 的熟悉度又更上一層樓了!💪

最後附上這張圖,算是快速的概覽 flexbox 語法

參考資料:

深入解析 CSS Flexbox