【總結(jié)】1097- 使用Flex實(shí)現(xiàn)常見(jiàn)布局的思路總結(jié)

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

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

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

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

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

可以看到頭和尾都沒(méi)了,這是因?yàn)閒lex-shrink的原因,這個(gè)也是flex子元素上的屬性,用來(lái)控制當(dāng)子元素的尺寸之和已經(jīng)超過(guò)容器了要怎么收縮元素,默認(rèn)值為1,就是按比例減去要收縮的空間,理論上是這樣,但實(shí)際上并沒(méi)有這么簡(jiǎn)單,接下來(lái)簡(jiǎn)單測(cè)試一下:
容器元素body為800px高,上中下高度分別為100+1000+100=1200px,根據(jù)1:1:1的flex-shrink計(jì)算總權(quán)重為1*100+1*1000+1*100=1200,子元素總高度超過(guò)容器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)容高度沒(méi)有變,這是為啥呢?上面我們提到了max-content,同樣,這里對(duì)應(yīng)著min-content的概念,雖然正常來(lái)說(shuō)應(yīng)該變成我們計(jì)算出的尺寸才對(duì),但是減少到元素內(nèi)容的min-content后它就不會(huì)再變小了。
content元素有個(gè)高度為1000的子元素,這個(gè)高度就是它的min-content,所以它不會(huì)縮小,它一個(gè)元素就比容器元素高了,再加上頭和尾因?yàn)槎紱](méi)有內(nèi)容,所以雖然理論上它們不是為0的。
但是為了更好的顯示效果,瀏覽器就直接把它們減少到0了,我們可以隨便給頭和尾加一點(diǎn)文字,文字的高度就會(huì)變成它們的min-content,它們的高度也就不會(huì)變成0:

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

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

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

隔行交叉顯示

有時(shí)候?yàn)榱瞬蛔尣季痔珕握{(diào),即使一個(gè)列表是同類(lèi)數(shù)據(jù),展示上也會(huì)做成上述隔行交叉的樣式,這個(gè)使用flex可以輕松的做到,給2n的行設(shè)置flex-direction: row-reverse即可讓偶數(shù)行的主軸方向由默認(rèn)的從左向右變成從右向左:

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

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

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

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

圣杯布局

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

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

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

這個(gè)場(chǎng)景使用flex完全不需要額外設(shè)置什么屬性,只要給容器元素設(shè)置display:flex即可,因?yàn)閒lex容器有個(gè)屬性align-items,用來(lái)設(shè)置flex子元素在交叉軸上如何對(duì)齊,默認(rèn)值為stretch,即如果flex子元素未設(shè)置高度,那么將占滿(mǎn)整個(gè)容器的高度,因?yàn)槲覀儾](méi)有給容器設(shè)置高度,所以容器的高度就是所有flex子元素里最大的高度。

小結(jié)
本文以標(biāo)題黨的名義總結(jié)了部分常見(jiàn)布局使用flex的實(shí)現(xiàn),要靈活使用flex還是需要理解它的一些屬性的意義,此外也需要知道flex的邊界在哪,哪些是它不擅長(zhǎng)的。
本文總結(jié)的難免會(huì)有不全,或者有更好的實(shí)現(xiàn),歡迎討論。

回復(fù)“加群”與大佬們一起交流學(xué)習(xí)~
點(diǎn)擊“閱讀原文”查看 120+ 篇原創(chuàng)文章
