天天看點

CSS flex 布局快速入門

以前已經學過flex了,一直沒做筆記,現在做下筆記再回憶下。

首先,flex布局的迷之屬性們,如果一知半解,機械記憶的話,那不到半個月基本忘光光。先感受一下這12個flex布局屬性,是不是很“迷”人。

父容器屬性

  • flex-flow
  • flex-direction
  • flex-wrap
  • justify-content
  • align-items
  • align-content

子元素屬性

  • order
  • flex-grow
  • flex-shrink
  • flex-basis
  • flex
  • align-self

而目前很多flex教程主要以列舉屬性為主,缺乏對比和了解性脈絡。

是以,下面會通過我梳理的一個脈絡去了解flex布局,包括不同屬性的異同以及一些容易造成誤解的細節點,徹底弄懂flex布局。

一、flex彈性盒模型

對于某個元素隻要聲明了 display: flex; ,那麼這個元素就成為了彈性容器,具有flex彈性布局的特性。

  1. 每個彈性容器都有兩根軸:主軸和交叉軸,兩軸之間成90度關系。注意:水準的不一定就是主軸。
  2. 每根軸都有起點和終點,這對于元素的對齊非常重要。
  3. 彈性容器中的所有子元素稱為<彈性元素>,彈性元素永遠沿主軸排列。
  4. 彈性元素也可以通過 display:flex 設定為另一個彈性容器,形成嵌套關系。是以一個元素既可以是彈性容器也可以是彈性元素。

彈性容器的兩根軸非常重要,所有屬性都是作用于軸的。下面從軸入手,将所有flex布局屬性串起來了解。

二、主軸

flex布局是一種一維布局模型,一次隻能處理一個次元(一行或者一列)上的元素布局,作為對比的是二維布局CSS Grid布局,可以同時處理行和列上的布局。

也就是說,flex布局大部分的屬性都是作用于主軸的,在交叉軸上很多時候隻能被動地變化。

1. 主軸的方向

我們可以在彈性容器上通過 flex-direction 修改主軸的方向。如果主軸方向修改了,那麼:

  1. 交叉軸就會相應地旋轉90度。
  2. 彈性元素的排列方式也會發生改變,因為彈性元素永遠沿主軸排列。

flex-direction:row

flex-direction:column

flex-direction:row-reverse

flex-direction:column-reverse

2. 沿主軸的排列處理

彈性元素永遠沿主軸排列,那麼如果主軸排不下,該如何處理?

通過設定 flex-wrap: nowrap | wrap | wrap-reverse 可使得主軸上的元素不折行、折行、反向折行。

預設是 nowrap 不折行,難道任由元素直接溢出容器嗎?當然不會,那麼這裡就涉及到元素的彈性伸縮應對,下面會講到。

 wrap 折行,顧名思義就是另起一行,那麼折行之後行與行之間的間距(對齊)怎樣調整?這裡又涉及到交叉軸上的多行對齊。

 wrap-reverse 反向折行,是從容器底部開始的折行,但每行元素之間的排列仍保留正向。

CSS flex 布局快速入門

3. 一個複合屬性

flex-flow = flex-drection + flex-wrap      
 flex-flow 相當于規定了flex布局的“工作流(flow)”

      
flex-flow: row nowrap;      

三、元素如何彈性伸縮應對

當 flex-wrap: nowrap; 不折行時,容器寬度有剩餘/不夠分,彈性元素們該怎麼“彈性”地伸縮應對?

這裡針對上面兩種場景,引入兩個屬性(需應用在彈性元素上)

  1.  flex-shrink :縮小比例(容器寬度<元素總寬度時如何收縮)
  2.  flex-grow :放大比例(容器寬度>元素總寬度時如何伸展)

1. flex-shrink: 縮小比例

來看下以下場景,彈性容器 #container 寬度是200px,一共有三個彈性元素,寬度分别是50px、100px、120px。在不折行的情況下,此時容器寬度是明顯不夠配置設定的。

實際上, flex-shrink 預設為1,也就是當不夠配置設定時,元素都将等比例縮小,占滿整個寬度,如下圖。

#container {
    display: flex;
    flex-wrap: nowrap;  
}      

元素收縮的計算方法

真的是等比縮小(每個元素各減去70/3的寬度)嗎?這裡稍微深究一下它的收縮計算方法。

  1. 彈性元素1:50px→37.03px
  2. 彈性元素2:100px→74.08px
  3. 彈性元素3:120px→88.89px

先抛結論: flex-shrink: 1 并非嚴格等比縮小,它還會考慮彈性元素本身的大小。

  • 容器剩餘寬度: -70px 
  • 縮小因子的分母: 1*50 + 1*100 + 1*120 = 270  (1為各元素flex-shrink的值)
  • 元素1的縮小因子: 1*50/270 
  • 元素1的縮小寬度為縮小因子乘于容器剩餘寬度: 1*50/270 * (-70) 
  • 元素1最後則縮小為: 50px + (1*50/270 *(-70)) = 37.03px 

加入彈性元素本身大小作為計算方法的考慮因素,主要是為了避免将一些本身寬度較小的元素在收縮之後寬度變為0的情況出現。

2. flex-grow: 放大比例

同樣,彈性容器 #container 寬度是200px,但此時隻有兩個彈性元素,寬度分别是50px、100px。此時容器寬度是有剩餘的。

那麼剩餘的寬度該怎樣配置設定?而 flex-grow 則決定了要不要配置設定以及各個配置設定多少。

(1)在flex布局中,容器剩餘寬度預設是不進行配置設定的,也就是所有彈性元素的 flex-grow 都為0。

CSS flex 布局快速入門

(2)通過指定 flex-grow 為大于零的值,實作容器剩餘寬度的配置設定比例設定。

元素放大的計算方法

放大的計算方法并沒有與縮小一樣,将元素大小納入考慮。

僅僅按 flex-grow 聲明的份數算出每個需配置設定多少,疊加到原來的尺寸上。

  • 容器剩餘寬度: 50px 
  • 分成每份: 50px / (3+2) = 10px 
  • 元素1放大為: 50px + 3 * 10 = 80px 

無多餘寬度時,flex-grow無效

下圖中,彈性容器的寬度正好等于元素寬度總和,無多餘寬度,此時無論 flex-grow 是什麼值都不會生效。

同理,對于 flex-shrink ,在容器寬度有剩餘時也是不會生效的。是以這兩個屬性是針對兩種不同場景的互斥屬性。

四、彈性處理與剛性尺寸

在進行彈性處理之餘,其實有些場景我們更希望元素尺寸固定,不需要進行彈性調整。設定元素尺寸除了width和height以外,flex還提供了一個 flex-basis 屬性。

flex-basis

設定的是元素在主軸上的初始尺寸,所謂的初始尺寸就是元素在 flex-grow 和 flex-shrink 生效前的尺寸。

1. 與width/height的差別

首先以width為例進行比較。看下下面的例子。 #container {display:flex;} 。

<div id="container">
  <div>11111</div>
  <div>22222</div>
</div>      

(1) 兩者都為0

  • width: 0 —— 完全沒顯示
  • flex-basis: 0 —— 根據内容撐開寬度

(2) 兩者非0

  • width: 非0;
  • flex-basis: 非0

—— 數值相同時兩者等效

—— 同時設定,flex-basis優先級高

(3) flex-basis為auto

CSS flex 布局快速入門

flex-basis為auto時,如設定了width則元素尺寸由width決定;沒有設定則由内容決定

(4) flex-basis == 主軸上的尺寸 != width

CSS flex 布局快速入門
  • 将主軸方向改為:上→下
  • 此時主軸上的尺寸是元素的height
  • flex-basis == height

2. 常用的複合屬性 flex

這個屬性應該是最容易迷糊的一個,下面揭開它的真面目。

flex = flex-grow + flex-shrink + flex-basis

複合屬性,前面說的三個屬性的簡寫。

一些簡寫

  • flex: 1

     = 

    flex: 1 1 0%

  • flex: 2

     = 

    flex: 2 1 0%

  • flex: auto

     = 

    flex: 1 1 auto;

  • flex: none

     = 

    flex: 0 0 auto;

     // 常用于固定尺寸 不伸縮

flex:1 和 flex:auto 的差別

其實可以歸結于

flex-basis:0

flex-basis:auto

的差別。

flex-basis

是指定初始尺寸,當設定為0時(絕對彈性元素),此時相當于告訴

flex-grow

flex-shrink

在伸縮的時候不需要考慮我的尺寸;相反當設定為

auto

時(相對彈性元素),此時則需要在伸縮時将元素尺寸納入考慮。

是以從下圖(轉自W3C)可以看到絕對彈性元素如果

flex-grow

值是一樣的話,那麼他們的尺寸一定是一樣的。

CSS flex 布局快速入門

五、容器内如何對齊

前面講完了元素大小關系之後,下面是另外一個重要議題——如何對齊。可以發現上面的所有屬性都是圍繞主軸進行設定的,但在對齊方面則不得不加入作用于交叉軸上。需要注意的是這些對齊屬性都是作用于容器上。

1. 主軸上的對齊方式

justify-content

2. 交叉軸上的對齊方式

主軸上比較好了解,重點是交叉軸上。因為交叉軸上存在單行和多行兩種情況。

交叉軸上的單行對齊

align-items

預設值是

stretch

,當元素沒有設定具體尺寸時會将容器在交叉軸方向撐滿。

align-items

不為

stretch

時,此時除了對齊方式會改變之外,元素在交叉軸方向上的尺寸将由内容或自身尺寸(寬高)決定。

CSS flex 布局快速入門
CSS flex 布局快速入門
CSS flex 布局快速入門
CSS flex 布局快速入門

注意,交叉軸不一定是從上往下,這點再次強調也不為過。

交叉軸上的多行對齊

還記得可以通過

flex-wrap: wrap

使得元素在一行放不下時進行換行。在這種場景下就會在交叉軸上出現多行,多行情況下,flex布局提供了

align-content

屬性設定對齊。

align-content

align-items

比較類似,同時也比較容易迷糊。下面會将兩者對比着來看它們的異同。

首先明确一點:

align-content

隻對多行元素有效,會以多行作為整體進行對齊,容器必須開啟換行。

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

align-items: stretch | flex-start | flex-end | center | baseline           

在屬性值上,

align-content

align-items

多了兩個值:

space-between

space-around

align-content與align-items異同對比

align-items

一樣,

align-content:

預設值也是

stretch

。兩者同時都為

stretch

時,毫無懸念所有元素都是撐滿交叉軸。

#container {

align-items: stretch; align-content: stretch;

}      

當我們将align-items改為

flex-start

或者給彈性元素設定一個具體高度,此時效果是行與行之間形成了間距。

#container {
  align-items: flex-start;
  align-content: stretch;
}

/*或者*/
#container {
  align-content: stretch;
}
#container > div {
  height: 30px;
}      

為什麼?因為

align-content

會以整行為機關,此時會将整行進行拉伸占滿交叉軸;而

align-items

設定了高度或者頂對齊,在不能用高度進行拉伸的情況下,選擇了用間距。

CSS flex 布局快速入門

嘗試把

align-content

設定為頂對齊,此時以行為機關,整體高度通過内容撐開。

align-items

僅僅管一行,是以在隻有第一個元素設定了高度的情況下,第一行的其他元素遵循

align-items: stretch

也被拉伸到了50px。而第二行則保持高度不變。

#container {
  align-items: stretch;
  align-content: flex-start;
}
#container > div:first-child {
    height: 50px;
}      
CSS flex 布局快速入門

兩者的差別還是不明顯?來看下面這個例子。

這裡僅對第二個元素的高度進行設定,其他元素高度則仍保持内容撐開。

CSS flex 布局快速入門

以第一個圖為例,會發現

align-content

會将所有行進行頂對齊,然後第一行由于第二個元素設定了較高的高度,是以展現出了底對齊。

兩者差異總結:

  • 兩者“作用域”不同
  • align-content管全局(所有行視為整體)
  • align-items管單行

能否更靈活地設定交叉軸對齊

除了在容器上設定交叉軸對齊,還可以通過

align-self

單獨對某個元素設定交叉軸對齊方式。

  1. 值與

    align-items

    相同
  2. 可覆寫容器的

    align-items

    屬性
  3. 預設值為

    auto

    ,表示繼承父元素的

    align-items

    屬性
CSS flex 布局快速入門
#container {
  display: flex;
  align-items: flex-start;
}

#container > div:first-child {
  align-self: stretch;
}

#container > div:nth-child(3) {
  align-self: center;
}

#container > div:nth-child(4) {
  align-self: flex-end;
}      

六、其他

order:更優雅地調整元素順序

CSS flex 布局快速入門
#container > div:first-child {
  order: 2;
}
#container > div:nth-child(2) {
  order: 4;
}
#container > div:nth-child(3) {
  order: 1;
}
#container > div:nth-child(4) {
  order: 3;
}      

order:可設定元素之間的排列順序

  1. 數值越小,越靠前,預設為0
  2. 值相同時,以dom中元素排列為準

七、總結

CSS flex 布局快速入門
CSS flex 布局快速入門
CSS flex 布局快速入門
CSS flex 布局快速入門