Flex 實現常見佈局的彙總

來源 | https://www.cnblogs.com/wanglinmantan/p/15092001.html

flex 全稱 Flexible Box 模型,顧名思義就是靈活的盒子,不過一般都叫彈性盒子,所有 PC 端及手機端現代瀏覽器都支持,所以不用擔心它的兼容性,有了這玩意,媽媽再也不用擔心我們的佈局。

先簡單介紹一下,要使用 flex 佈局,需要先給一個容器元素設置 display:flex 讓它變成 flex 容器。

然後其所有的直接子元素就變成 flex 子元素了,在 flex 裏存在兩根軸,叫主軸和交叉軸,互相垂直,主軸默認水平,flex 子元素默認會沿主軸排列,可以控制 flex 子元素在主軸上伸縮,主軸方向可以設置,相關的 css 屬性分爲兩類,一類是給 flex 容器設置的,一類是給 flex 子元素設置的,本文在介紹一些典型場景實現的同時也會順帶講解部分屬性,當然更詳細的內容可以閱讀 MDN 上的教程。

單列布局

單列布局是最簡單的佈局了,從上到下排列,如圖:

可以使用三個 div 來表示頭、內容和尾,然後把外層容器,即 body 設爲 flex 容器,因爲 flex 默認的主軸是水平的,我們需要把它設置爲垂直的,然後再設置元素在交叉軸居中即可:

當然更常見的情況是內容高度不確定,這樣我們往往會希望在內容高度不滿一屏時底部內容挨着底邊,超過一屏時跟在最後,這首先需要容器元素有固定的高度,否則何來底邊,我們可以把 html 和 body 的高度都設爲 100%,然後去掉給 content 元素設置的高度,並給它添加一個帶高度的子元素:

接下來需要使用到 flex-grow 屬性,這個是 flex 子元素上的屬性,用來控制容器還有空間剩餘時,flex 子元素怎麼進行擴展,默認值是 0,也就是不擴展,子元素會顯示爲它們默認的大小,這個所謂的默認大小分幾種情況:

1、如果子元素的另一個屬性 flex-basis 設置了不爲 auto 的具體數值,那麼無論元素有沒有設置具體大小都顯示爲該屬性定義的尺寸;

2、如果子元素的 flex-basis 的值爲 auto(默認值),那麼如果元素設置了具體的大小那麼顯示爲該設置的尺寸;

3、否則取決於元素內容的 max-content 大小;

當 flex-grow 設爲一個正數時,那麼各個子元素會按設置的份數來按比例分配剩餘可用的空間,比如剩餘空間爲 90px,三個子元素該屬性值都設爲 1,那麼每個元素將在原來大小的基礎上加上 90/3=30px。

根據上述原理,我們只需要給 content 元素的 flex-grow 屬性設爲 1 即可,其他都是 0,所以剩餘空間將全給 content 元素:

這樣內容不足時底部就可以挨着底邊了,但是當內容過多,超過一屏時:

可以看到頭和尾都沒了,這是因爲 flex-shrink 的原因,這個也是 flex 子元素上的屬性,用來控制當子元素的尺寸之和已經超過容器了要怎麼收縮元素,默認值爲 1,就是按比例減去要收縮的空間,理論上是這樣,但實際上並沒有這麼簡單,接下來簡單測試一下:

容器元素 body 爲 800px 高,上中下高度分別爲 100+1000+100=1200px,根據 1:1:1 的 flex-shrink 計算總權重爲 1100+11000+1*100=1200,子元素總高度超過容器 400px,這多出的要按的比例從各自高度中減去,具體爲:

也就是分別都減去上述值,減完後理論上各自的高度變成了 66.67px、666.67.67px、66.67px,但是實際上:

可以看到頭和尾都變成了 0,內容高度沒有變,這是爲啥呢?上面我們提到了 max-content,同樣,這裏對應着 min-content 的概念,雖然正常來說應該變成我們計算出的尺寸纔對,但是減少到元素內容的 min-content 後它就不會再變小了。

content 元素有個高度爲 1000 的子元素,這個高度就是它的 min-content,所以它不會縮小,它一個元素就比容器元素高了,再加上頭和尾因爲都沒有內容,所以雖然理論上它們不是爲 0 的。

但是爲了更好的顯示效果,瀏覽器就直接把它們減少到 0 了,我們可以隨便給頭和尾加一點文字,文字的高度就會變成它們的 min-content,它們的高度也就不會變成 0:

所以這就意味着不要想着去精確計算,把它交給瀏覽器,瀏覽器會給你以最好的方式呈現。

那麼解決頭和尾不要消失的問題很簡單,可以給它們也加個固定高度的子元素,但是最簡單的方法是把它們的 flex-shrink 設爲 0,也就是不收縮:

這樣就實現我們的需求了。

經典導航欄

如圖所示是一個經典的網站導航欄的佈局,logo 和導航在左,用戶信息在右,不用 flex 可能會使用浮動,上圖使用浮動還好,但是如果右邊是兩個塊,那麼右邊浮動的元素的顯示順序和書寫順序要不一致纔行,或者再用一個元素包裹一下,使用 flex 則沒有這種煩惱。

該場景可以使用一個容器來包裹左邊的 logo 和導航,再設置 justify-content:space-between 來實現,但是有個小技巧可以不用這個包裹元素,就是利用 margin 的 auto 值。

回憶一下我們以前水平居中都是怎麼做的,是不是這樣 margin:0 auto,margin-left 和 margin-right 的默認值是 0,如果設置爲 auto,將會根據剩餘可用空間來計算,這也是爲什麼能水平居中。

因爲左右都想盡量多,那麼就只能平分了,對於本示例,我們只給用戶名 flex 子元素設置 margin-left:auto,那麼剩餘空間將全部給它,也就相當於把用戶塊擠到右邊去了:

隔行交叉顯示

有時候爲了不讓佈局太單調,即使一個列表是同類數據,展示上也會做成上述隔行交叉的樣式,這個使用 flex 可以輕鬆的做到,給 2n 的行設置 flex-direction: row-reverse 即可讓偶數行的主軸方向由默認的從左向右變成從右向左:

此外也可以使用 order 屬性,這個屬性可以讓 flex 子元素按 order 的數值大小來排序顯示,我們可以默認左邊的設爲 2,右邊的設爲 3,然後在偶數行再給右邊的設爲 1,自然就跑到前面去了:

網格佈局

此網格非 grid 佈局,雖然網格列表用 grid 是最好的,但是本文的主角是 flex,假設我們要實現下面這樣一個列表:

上述列表對 flex 來說是不擅長的,因爲要帶間距,所以不能簡單的把子元素寬度設爲 25%,否則再加上外邊距,一行肯定顯示不下四個,那你可能會想,那我寬度就少一點好了,比如設爲 20%,然後允許擴展,即 flex-grow:1,那樣不就可以把減去子元素寬度及外邊距還剩下的空間再還給子元素了嗎,試試看:

可以看到前面一切正常,但是最後一行因爲只有一個元素,且設置了允許擴展,所以它被拉滿整行了,這種效果顯然不是我們要的。

其實我們可以使用內邊距來做間距,設置一下子元素的 box-sizing:border-box,讓內邊距包含在寬度內,這樣就可以放心的把子元素的寬度設爲 25% 了,當然這樣的缺點是裏面得再嵌套一個元素用來作爲實際的內容容器。

聖盃佈局

所謂聖盃佈局如上所示,頭尾高度固定,寬度佔滿,中間的內容部分分爲三列,兩側寬度固定,高度佔滿,中間的內容部分隨着瀏覽器寬度變化,其實就是我們上面講過的【單列布局】的中間部分變成三列而已,實現完全沒有啥特別的,以【單列布局】爲基礎,給 content 添加三個子元素,兩側定寬,並且不允許收縮,中間允許擴展即可:

垂直居中

不知道各位最開始用 flex 是爲什麼,反正筆者就是衝着垂直居中去的,用它實在是太簡單了,之前還考慮是不是定高呀,用什麼定位呀,用 flex 就是兩步,一讓父元素變成彈性盒子,二設置交叉軸的元素排布方式爲居中就完事了:

如果還需要水平居中的話就再給容器元素設置主軸的排列方式爲 justify-content:center,現在連讓文字居中我都是用 flex,無情的拋棄了 text-align 和 line-height。

高度自動對齊

有些時候同一列的元素爲了美觀我們希望他們的高度是一樣的,如果內容固定不變當然可以直接寫死高度,但如果可變的話就不能寫死了:

這個場景使用 flex 完全不需要額外設置什麼屬性,只要給容器元素設置 display:flex 即可,因爲 flex 容器有個屬性 align-items,用來設置 flex 子元素在交叉軸上如何對齊,默認值爲 stretch,即如果 flex 子元素未設置高度,那麼將佔滿整個容器的高度,因爲我們並沒有給容器設置高度,所以容器的高度就是所有 flex 子元素裏最大的高度。

小結

本文以標題黨的名義總結了部分常見佈局使用 flex 的實現,要靈活使用 flex 還是需要理解它的一些屬性的意義,此外也需要知道 flex 的邊界在哪,哪些是它不擅長的。

本文總結的難免會有不全,或者有更好的實現,歡迎討論。

本文由 Readfog 進行 AMP 轉碼,版權歸原作者所有。
來源https://mp.weixin.qq.com/s/5t2MX2DYS1BKxRZJXJMqPQ