Flex實(shí)現(xiàn)常見布局的匯總

來源 | https://www.cnblogs.com/wanglinmantan/p/15092001.html
flex全稱Flexible Box模型,顧名思義就是靈活的盒子,不過一般都叫彈性盒子,所有PC端及手機(jī)端現(xiàn)代瀏覽器都支持,所以不用擔(dān)心它的兼容性,有了這玩意,媽媽再也不用擔(dān)心我們的布局。
先簡單介紹一下,要使用flex布局,需要先給一個容器元素設(shè)置display:flex讓它變成flex容器。
然后其所有的直接子元素就變成flex子元素了,在flex里存在兩根軸,叫主軸和交叉軸,互相垂直,主軸默認(rèn)水平,flex子元素默認(rèn)會沿主軸排列,可以控制flex子元素在主軸上伸縮,主軸方向可以設(shè)置,相關(guān)的css屬性分為兩類,一類是給flex容器設(shè)置的,一類是給flex子元素設(shè)置的,本文在介紹一些典型場景實(shí)現(xiàn)的同時也會順帶講解部分屬性,當(dāng)然更詳細(xì)的內(nèi)容可以閱讀MDN上的教程。
單列布局
單列布局是最簡單的布局了,從上到下排列,如圖:

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

當(dāng)然更常見的情況是內(nèi)容高度不確定,這樣我們往往會希望在內(nèi)容高度不滿一屏?xí)r底部內(nèi)容挨著底邊,超過一屏?xí)r跟在最后,這首先需要容器元素有固定的高度,否則何來底邊,我們可以把html和body的高度都設(shè)為100%,然后去掉給content元素設(shè)置的高度,并給它添加一個帶高度的子元素:

接下來需要使用到flex-grow屬性,這個是flex子元素上的屬性,用來控制容器還有空間剩余時,flex子元素怎么進(jìn)行擴(kuò)展,默認(rèn)值是0,也就是不擴(kuò)展,子元素會顯示為它們默認(rèn)的大小,這個所謂的默認(rèn)大小分幾種情況:
1、如果子元素的另一個屬性flex-basis設(shè)置了不為auto的具體數(shù)值,那么無論元素有沒有設(shè)置具體大小都顯示為該屬性定義的尺寸;
2、如果子元素的flex-basis的值為auto(默認(rèn)值),那么如果元素設(shè)置了具體的大小那么顯示為該設(shè)置的尺寸;
3、否則取決于元素內(nèi)容的max-content大小;
當(dāng)flex-grow設(shè)為一個正數(shù)時,那么各個子元素會按設(shè)置的份數(shù)來按比例分配剩余可用的空間,比如剩余空間為90px,三個子元素該屬性值都設(shè)為1,那么每個元素將在原來大小的基礎(chǔ)上加上90/3=30px。
根據(jù)上述原理,我們只需要給content元素的flex-grow屬性設(shè)為1即可,其他都是0,所以剩余空間將全給content元素:

這樣內(nèi)容不足時底部就可以挨著底邊了,但是當(dāng)內(nèi)容過多,超過一屏?xí)r:

可以看到頭和尾都沒了,這是因為flex-shrink的原因,這個也是flex子元素上的屬性,用來控制當(dāng)子元素的尺寸之和已經(jīng)超過容器了要怎么收縮元素,默認(rèn)值為1,就是按比例減去要收縮的空間,理論上是這樣,但實(shí)際上并沒有這么簡單,接下來簡單測試一下:
容器元素body為800px高,上中下高度分別為100+1000+100=1200px,根據(jù)1:1:1的flex-shrink計算總權(quán)重為1*100+1*1000+1*100=1200,子元素總高度超過容器400px,這多出的要按的比例從各自高度中減去,具體為:
(400*1*100)/1200=33.33px(400*1*1000)/1200=333.33px(400*1*100)/1200=33.33px
也就是分別都減去上述值,減完后理論上各自的高度變成了66.67px、666.67.67px、66.67px,但是實(shí)際上:

可以看到頭和尾都變成了0,內(nèi)容高度沒有變,這是為啥呢?上面我們提到了max-content,同樣,這里對應(yīng)著min-content的概念,雖然正常來說應(yīng)該變成我們計算出的尺寸才對,但是減少到元素內(nèi)容的min-content后它就不會再變小了。
content元素有個高度為1000的子元素,這個高度就是它的min-content,所以它不會縮小,它一個元素就比容器元素高了,再加上頭和尾因為都沒有內(nèi)容,所以雖然理論上它們不是為0的。
但是為了更好的顯示效果,瀏覽器就直接把它們減少到0了,我們可以隨便給頭和尾加一點(diǎn)文字,文字的高度就會變成它們的min-content,它們的高度也就不會變成0:

所以這就意味著不要想著去精確計算,把它交給瀏覽器,瀏覽器會給你以最好的方式呈現(xiàn)。
那么解決頭和尾不要消失的問題很簡單,可以給它們也加個固定高度的子元素,但是最簡單的方法是把它們的flex-shrink設(shè)為0,也就是不收縮:

這樣就實(shí)現(xiàn)我們的需求了。
經(jīng)典導(dǎo)航欄

如圖所示是一個經(jīng)典的網(wǎng)站導(dǎo)航欄的布局,logo和導(dǎo)航在左,用戶信息在右,不用flex可能會使用浮動,上圖使用浮動還好,但是如果右邊是兩個塊,那么右邊浮動的元素的顯示順序和書寫順序要不一致才行,或者再用一個元素包裹一下,使用flex則沒有這種煩惱。
該場景可以使用一個容器來包裹左邊的logo和導(dǎo)航,再設(shè)置justify-content:space-between來實(shí)現(xiàn),但是有個小技巧可以不用這個包裹元素,就是利用margin的auto值。
回憶一下我們以前水平居中都是怎么做的,是不是這樣margin:0 auto,margin-left和margin-right的默認(rèn)值是0,如果設(shè)置為auto,將會根據(jù)剩余可用空間來計算,這也是為什么能水平居中。
因為左右都想盡量多,那么就只能平分了,對于本示例,我們只給用戶名flex子元素設(shè)置margin-left:auto,那么剩余空間將全部給它,也就相當(dāng)于把用戶塊擠到右邊去了:

隔行交叉顯示

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

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

網(wǎng)格布局
此網(wǎng)格非grid布局,雖然網(wǎng)格列表用grid是最好的,但是本文的主角是flex,假設(shè)我們要實(shí)現(xiàn)下面這樣一個列表:

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

可以看到前面一切正常,但是最后一行因為只有一個元素,且設(shè)置了允許擴(kuò)展,所以它被拉滿整行了,這種效果顯然不是我們要的。
其實(shí)我們可以使用內(nèi)邊距來做間距,設(shè)置一下子元素的box-sizing:border-box,讓內(nèi)邊距包含在寬度內(nèi),這樣就可以放心的把子元素的寬度設(shè)為25%了,當(dāng)然這樣的缺點(diǎn)是里面得再嵌套一個元素用來作為實(shí)際的內(nèi)容容器。

圣杯布局

所謂圣杯布局如上所示,頭尾高度固定,寬度占滿,中間的內(nèi)容部分分為三列,兩側(cè)寬度固定,高度占滿,中間的內(nèi)容部分隨著瀏覽器寬度變化,其實(shí)就是我們上面講過的【單列布局】的中間部分變成三列而已,實(shí)現(xiàn)完全沒有啥特別的,以【單列布局】為基礎(chǔ),給content添加三個子元素,兩側(cè)定寬,并且不允許收縮,中間允許擴(kuò)展即可:

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

如果還需要水平居中的話就再給容器元素設(shè)置主軸的排列方式為justify-content:center,現(xiàn)在連讓文字居中我都是用flex,無情的拋棄了text-align和line-height。
高度自動對齊
有些時候同一列的元素為了美觀我們希望他們的高度是一樣的,如果內(nèi)容固定不變當(dāng)然可以直接寫死高度,但如果可變的話就不能寫死了:

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

小結(jié)
本文以標(biāo)題黨的名義總結(jié)了部分常見布局使用flex的實(shí)現(xiàn),要靈活使用flex還是需要理解它的一些屬性的意義,此外也需要知道flex的邊界在哪,哪些是它不擅長的。
本文總結(jié)的難免會有不全,或者有更好的實(shí)現(xiàn),歡迎討論。
學(xué)習(xí)更多技能
請點(diǎn)擊下方公眾號
![]()

