詳解flex布局的元素如何分配容器的剩余空間

2019-10-16 23:28:49 來源:互聯(lián)網(wǎng)作者:小時(shí)光 人氣: 次閱讀 522 條評(píng)論

文章主要介紹了詳解flex布局的元素如何分配容器的剩余空間,文中通過示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來一起學(xué)習(xí)...

文章主要介紹了詳解flex布局的元素如何分配容器的剩余空間,文中通過示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧

自從開始開學(xué)習(xí) CSS 布局,想要比較靈活的把父元素的空間分配給各個(gè)子元一直是各個(gè)前端程序員的夢(mèng)想。

在 flex 之前,如果不是專門去搜索相關(guān)的解決方案,一般人幾乎想不出非常靈活的三(多)欄等高布局方案,而即使看了解決方案,很多人也會(huì)大呼奇技淫巧。

不得不感慨在 flex 之前 CSS 的布局功能之弱:基本只能使用一些并非為布局而設(shè)計(jì)的屬性來實(shí)現(xiàn)想要的布局——float、inline-block、position、甚至是 table 等。而使用這些屬性來實(shí)現(xiàn)各種布局效果,往往又會(huì)遇到相當(dāng)多另外的坑:比如浮動(dòng)的閉合、inline-block 的垂直對(duì)齊、position 的定位原點(diǎn)以及 table 的不夠靈活等。

直到出現(xiàn)了 flex

flex 可以說是一次性解決了前端布局的所有的問題(當(dāng)然,并沒有完全解決,要不然也不會(huì)有 grid layout 了),以前很難實(shí)現(xiàn)的布局效果在 flex 下簡直不能更簡單,以至于一些其它平臺(tái)也開始吸納 flex 的布局思想,也有些開源項(xiàng)目把 flex 的布局方式移植到其它平臺(tái)。

中文社區(qū)也有不少寫 flex 的文章,比如 ruanyifeng。然而個(gè)人覺得不少寫 flex 的文章都有個(gè)通病,就是一上來就整一堆 flex 相關(guān)的術(shù)語,比如 flex container,flex item,main axis(主軸),cors axis(交叉軸),不禁讓人望而生畏,都還沒搞清楚怎么回事,就來一堆術(shù)語。

然而這還不是最大的問題,最大的問題是很多文章并沒有把 flex 布局的詳細(xì)計(jì)算方式講清楚,尤其是連 ruanyifeng 的文章也沒把這事說清楚,但是在 Google 搜索 flex 相關(guān)的文章,他的文章卻會(huì)出現(xiàn)在第一頁。因?yàn)槲矣X得他寫的并不好,所以就不貼地址了,想看的同學(xué)可以自己搜一下,就在第一頁。

即使是 MDN 以及《The Book Of CSS3》里也沒把 flex-grow 和 flex-shrink 的計(jì)算方式說清楚。

所以我決定寫這一篇文章,把 flex-grow 與 flex-shrink 的詳細(xì)計(jì)算方式講清楚。

flex 如何解決傳統(tǒng)常見布局問題

在傳統(tǒng)布局中最常見也是急需的當(dāng)然就是在從左往右把父元素的空間分配給子元素以實(shí)現(xiàn)多欄布局了:按比例也好,定寬也好,更靈活的定寬加占用剩余空間也好。

那我們就從使用 flex 如何實(shí)現(xiàn)三欄布局開始吧。

想要實(shí)現(xiàn)三欄等高布局,且兩邊的側(cè)欄寬度固定而中間一欄占用剩余的空間,如下代碼就足夠了:

<style>
  section {display: flex;}
  .left-side,
  .right-side {width: 200px;}
  .content {flex-grow: 1;}
</style>
<section>
  <div class="left-side"></div>
  <div class="content"></div>
  <div class="right-side"></div>
</section>

其中 section 元素的寬度將會(huì)像 block 元素一樣盡量的寬,對(duì)外面的元素來說,它的行為很像一個(gè) block 塊。三個(gè)元素會(huì)從左往右占據(jù)父元素的空間(這很顯然)。左右側(cè)邊欄的寬度都是 200px,中間 .content 元素的寬度將會(huì)占據(jù) section 元素的剩余寬度。

另外,section 的高度會(huì)自動(dòng)被最高的一個(gè)子元素?fù)伍_,同時(shí)其它子元素的高度也會(huì)被拉到跟 section 元素一樣高,而如果給 section 元素設(shè)置了高度,而所有子元素的高度設(shè)置為 auto ,所有的子元素也都會(huì)自動(dòng)跟父元素一樣高,這簡直就是在傳統(tǒng)布局中做夢(mèng)都想要的功能!

總之,在高度方面,flex 的表現(xiàn)是相當(dāng)符合直覺的。

另外,如果不給 flex 子元素設(shè)置寬度和 flex-grow,它會(huì)盡量的窄。

flex-grow 的計(jì)算方式

上面 demo 中最值得注意的是 .content 元素的 flex-grow 屬性,設(shè)置為 1 它就可以占滿水平剩余空間。這也是本文的重點(diǎn):講清 flex-grow 與 flex-shrink 屬性的詳細(xì)計(jì)算方式。

flex-grow 屬性決定了父元素在空間分配方向上還有剩余空間時(shí),如何分配這些剩余空間。其值為一個(gè)權(quán)重(也稱擴(kuò)張因子),默認(rèn)為 0(純數(shù)值,無單位),剩余空間將會(huì)按照這個(gè)權(quán)重來分配。

比如剩余空間為 x,三個(gè)元素的 flex-grow 分別為 a,b,c。設(shè) sum 為 a + b + c。那么三個(gè)元素將得到剩余空間分別是 x a / sum, x b / sum, x * c / sum,是為權(quán)重也。

舉個(gè)例子:

父元素寬度 500px,三個(gè)子元素的 width 分別為 100px,150px,100px。

于是剩余空間為 150px

三個(gè)元素的 flex-grow 分別是 1,2,3,于是 sum 為 6
則三個(gè)元素所得到的多余空間分別是:

150 * 1 / 6 = 25px
150 * 2 / 6 = 50px
150 * 3 / 6 = 75px
三個(gè)元素最終的寬度分別為 125px,200px,175px。

100px + 25px = 125px
150px + 50px = 200px
100px + 75px = 175px
可以打開這個(gè) demo(下文中所有的 demo 都在這個(gè)頁面) 然后用開發(fā)工具查看一下。注意不要用截圖工具量,可能量不準(zhǔn),因?yàn)楦叻制梁头糯蟮戎T多因素都會(huì)影響測量結(jié)果。

然而!不止這些,還有一種情況:

當(dāng)所有元素的 flex-grow 之和小于 1 的時(shí)候(注意是 1,也就是說每個(gè)元素的 flex-grow 都是一個(gè)小數(shù)如 0.2 這樣的),上面式子中的 sum 將會(huì)使用 1 來參與計(jì)算,而不論它們的和是多少。也就是說,當(dāng)所有的元素的 flex-grow 之和小于 1 的時(shí)候,剩余空間不會(huì)全部分配給各個(gè)元素。

實(shí)際上用來分配的空間是 sum(flex-grow) / 1 * 剩余空間,這些用來分配的空間依然是按 flex-grow 的比例來分配。

還是上面一個(gè)例子,但是三個(gè)元素的 flex-grow 分別是 0.1,0.2,0.3,那么計(jì)算公式將變成下面這樣:

150 * 0.1 / 1 = 15px
150 * 0.2 / 1 = 30px
150 * 0.3 / 1 = 45px
150px - 15px - 30px - 45px = 60px,即還有 60px 沒有分配給任何子元素。

三個(gè)元素的最終寬度分別為:

100px + 15px = 115px
150px + 30px = 180px
100px + 45px = 145px

如上所述即是 flex-grow 的計(jì)算方式。

另外,flex-grow 還會(huì)受到 max-width 的影響。如果最終 grow 后的結(jié)果大于 max-width 指定的值,max-width 的值將會(huì)優(yōu)先使用。同樣會(huì)導(dǎo)致父元素有部分剩余空間沒有分配。

flex-shrink 的計(jì)算方式

前文已經(jīng)說到,flex 幾乎一次性解決了前端布局的所有問題。

那么既然可以在空間有多余時(shí)把多余空間分配給各個(gè)子元素,當(dāng)然也可以在空間不夠時(shí)讓各個(gè)子元素收縮以適應(yīng)有限的空間了。

這就是 flex-shrink 屬性的作用。

你可能會(huì)覺得 flex-shrink 的計(jì)算方式跟 flex-grow 很類似,然而事情并沒有這么簡單。

flex-shrink 屬性定義空間不夠時(shí)各個(gè)元素如何收縮。其值默認(rèn)為 1。很多文章對(duì)此基本是一筆帶過:“flex-shrink 屬性定義了元素的收縮系數(shù)”,根本就不說它具體是怎么計(jì)算的。

flex-shrink 定義的僅僅只是元素寬度變小的一個(gè)權(quán)重分量。

每個(gè)元素具體收縮多少,還有另一個(gè)重要因素,即它本身的寬度。

舉個(gè)例子:

父元素 500px。三個(gè)子元素分別設(shè)置為 150px,200px,300px。

三個(gè)子元素的 flex-shrink 的值分別為 1,2,3。

首先,計(jì)算子元素溢出多少:150 + 200 + 300 - 500 = -150px。

那這 -150px 將由三個(gè)元素的分別收縮一定的量來彌補(bǔ)。

具體的計(jì)算方式為:每個(gè)元素收縮的權(quán)重為其 flex-shrink 乘以其寬度。

所以總權(quán)重為 1 150 + 2 200 + 3 * 300 = 1450

三個(gè)元素分別收縮:

150 1(flex-shrink) 150(width) / 1450 = -15.5
150 2(flex-shrink) 200(width) / 1450 = -41.4
150 3(flex-shrink) 300(width) / 1450 = -93.1

三個(gè)元素的最終寬度分別為:

150 - 15.5 = 134.5
200 - 41.4 = 158.6
300 - 93.1 = 206.9

同樣,當(dāng)所有元素的 flex-shrink 之和小于 1 時(shí),計(jì)算方式也會(huì)有所不同:

此時(shí),并不會(huì)收縮所有的空間,而只會(huì)收縮 flex-shrink 之和相對(duì)于 1 的比例的空間。

還是上面的例子,但是 flex-shrink 分別改為 0.1,0.2,0.3。

于是總權(quán)重為 145(正好縮小 10 倍,略去計(jì)算公式)。

三個(gè)元素收縮總和并不是 150px,而是只會(huì)收縮 150px 的 (0.1 + 0.2 + 0.3) / 1 即 60% 的空間:90px。

每個(gè)元素收縮的空間為:

90 0.1(flex-shrink) 150(width) / 145 = 9.31
90 0.2(flex-shrink) 200(width) / 145 = 24.83
90 0.3(flex-shrink) 300(width) / 145 = 55.86

三個(gè)元素的最終寬度分別為:

150 - 9.31 = 140.69
200 - 24.83 = 175.17
300 - 55.86 = 244.14

當(dāng)然,類似 flex-grow,flex-shrink 也會(huì)受到 min-width 的影響。

總結(jié)

雖然上面的公式看起來很復(fù)雜,其實(shí)計(jì)算過程還是比較簡單的:如果所有元素的 flex-grow/shrink 之和大于等于 1,則所有子元素的尺寸一定會(huì)被調(diào)整到適應(yīng)父元素的尺寸(在不考慮 max/min-width/height 的前提下),而如果 flex-grow/shrink 之和小于 1,則只會(huì) grow 或 shrink 所有元素 flex-grow/shrink 之和相對(duì)于 1 的比例。grow 時(shí)的每個(gè)元素的權(quán)重即為元素的 flex-grow 的值;shrink 時(shí)每個(gè)元素的權(quán)重則為元素 flex-shrink 乘以 width 后的值。

您可能感興趣的文章

相關(guān)文章