1. 程式人生 > >Flex基本原理(圖示解析)

Flex基本原理(圖示解析)

新版的flexbox規範分兩部分:一部分是container,一部分是 items。 flexbox是一整套佈局規範,包含了多個css屬性,所以學習起來比`float: left;` 這樣簡單的佈局要複雜很多。

基本原理


容器上有 主軸和縱軸的概念,預設主軸(main-axis)是橫向,從左到右,縱軸是豎向,從上到下。其中所有的孩子的佈局都會受到這兩個軸的影響。後面會講到,有很多相關的css屬性就是通過改變主軸和縱軸的方向來實現不同的佈局效果的。 div上設定 `display: flex` 或者 `display: inline-flex` 來變成一個flex容器。然後給其中的每一個孩子設定 `flex: [number]` 來讓他們按比例分配容器的寬度。 比如三個item分別設定了 `flex: 1` `flex: 2` `flex: 1` 則他們是按照 1-2-1 的比例來分配寬度的。 如果有item沒有設定 `flex` 而是設定了寬度,比如 `width: 100px` 那麼它的寬度就不受flex容器的影響,但是其他的設定了 `flex: [number]` 的容器會按比例平分剩下的部分。即使通過 `width: 100px` 的方式定死寬度,除了和 flex: 1 的寬度計算方式不同之外,其他並沒有不同。 其實應該說,如果一個孩子沒有通過 flex: 1的方式來定義寬度,那麼會根據非flex的方式來優先計算它的寬度,然後其他的聲明瞭flex的孩子再按比例分配剩下的寬度。 如下圖所示,其中只有第一個孩子聲明瞭 `flex:1` ,後面的都沒有申明寬度,那麼在flexbox容器中,後面的7個孩子都是由他們中的文字撐開的寬度,剩餘的寬度全部分配給第一個孩子。

後面還會仔細講 `flex: 1` 定義孩子寬度的細節。

container

display `display: flex` 或者 `display: inline-flex` 來宣告一個flexbox容器 flex-direction

items的排列方向,其實就是改變了上面所說的 主軸方向,所以這個屬性會其他屬性產生影響,因為整個flexbox的佈局都是由主軸和縱軸決定的。 row: 從左到右 row-reverse: 從右到左 colum: 從上到下 colum-reverse: 從下到上 下面分別是這四個值的結果
flex-wrap 當內容超出之後是否折行。 nowrap:不換行,而是通過收縮每一個孩子的寬度來擠在一行。 wrap: 換行。 wrap-inverse: 換行,但是折行方向相反,(比如預設是折到下一行,但是這個屬性會導致折到上一行)。 如下圖所示,分別是上述的三個值的效果,其中每一個孩子寬度為 40px,8個孩子已經超出了容器的 200px寬度。

flex-flow flex-direction 和 flex-wrap 的組合寫法。 justify-content 不知道如何準確翻譯 justify 這個詞。它的作用是定義瞭如何分配剩餘的空白區域。 flex-start:主軸方向 flex-end: 主軸反方向 center: 擠在中間 space-between: 中間有空白。 space-around: 中間和兩邊都有空白。其中兩側的空白是中間的一半寬度,可以理解為每個孩子兩側都有相同寬度的空白,並且空白不合並。 上面說過,flex-direction 會改變主軸方向,下面我們看看 flex-direction 為預設情況下(從左到右)這五個不同值的區別:

發現一個bug,在chrome下,在偵錯程式下直接切換space-between  space-around 是不生效的。 然後我們設定一下 `flex-direction: column`,會發現他們的主軸已經變成從到上下了:
align-items 孩子的對齊方式,這個比較好理解。就是孩子的對齊方式。 注意,這裡的“對齊”指的是縱軸上的對齊方式,所以,這個屬性也是受 `flex-direction` 影響的。 比如我有一個高度為100 px的容器,那麼預設情況下,是scratch,就是在縱軸上填滿容器。
如上圖所示,只定義了容器高度,預設情況下孩子的高度就會填滿容器。 如果我改成`align-items: flexstart`  就會是這樣
align-items 所有屬性值如下: flex-start: 縱軸開始 flex-end: 縱軸結束 center: 縱軸居中 baseline: 縱軸的baseline scratch: 填滿容器。 align-content 當有多行內容的時候,這個屬性決定了如何對多行內容進行對齊。 注意,上面的每一個屬性都是以每一個item為單位進行佈局,而這個屬性是以一行為單位進行佈局。感覺這個屬性應該叫 align-lines 比較合適。 如果只有一行的話,這個屬性是不生效的,所以一定要配合 `flex-wrap: wrap` 來使用。

items

order 很簡單,排列順序,沒什麼好講的。 flex-grow 定義了主軸上,孩子分配剩餘空白區域的比例。 比如現在有三個孩子,寬度是 40px,並且`margin: 2px`,容器寬度 200px,那麼預設情況下,有 `(200-44)/3=68` 空白的寬度。
如果你給每個孩子都定義了 `flex-grow: 1`,那麼他們平分剩餘的空白區域,就是這樣:
這時候,如果我給第二個孩子定義 `flex-grow: 2`,那麼它的寬度是怎麼計算的? 其實就是把 68px 的空白按照`1:1:2` 的比例分配給他們,也就是 `17:17:34` 的大小來分配,所以三個孩子的寬度分別為 `57,57,74`:
如果你給一個孩子設定為 `flex-grow: 0` 那麼它不會參與剩餘空白的分配。 flex-shrink 如果孩子的總寬度超過了容器寬度(主軸),那麼這個屬性定義瞭如何把超過的那部分平分到每個孩子身上,然他們按比例來縮小一定的寬度從而可以在容器中裝得下。 假設我們現在有一個容器,寬度為 `200px`,有三個孩子寬度為 `100px`,並且每個海澤有 `margin: 2px`,那麼三個孩子總寬度 `(100+2*2)*3 = 312px` ,超過了 112px。 所以我們從這三個孩子身上砍下 `112px` 寬度,以讓他們能裝進容器中。 預設情況下,是 按 `1:1:1` 的比例來砍,也就是每個孩子各砍下 “112/3=37.3“,所以預設每個孩子寬度是 100-37.3=62.7。 那麼現在我們定義第二個孩子 `flex-shrink: 2`,其他兩個為 1,那麼他們會把 `112px` 的寬度按 `1:2:1` 來平分。於是第二個孩子寬度為 `100-(112/4*2)=44px` 寬度,另外兩個為`100-(112/4*1)=72px`:
flex-basis 在分配空白之前,孩子的寬度,預設是 `auto`,也就是孩子本身的寬度,如果你定義了 `flex-basis: 50px;` ,那麼它就會覆蓋掉孩子的寬度。 flex 他是 flex-grow, flex-shrink, flex-basis三個屬性的快捷方式。預設是 0, 1, auto。 強烈建議用這個屬性,而不是分別設定三個屬性,因為這個屬性可以“聰明地”設定三個屬性來達到你要的效果。 你可以認為 flex 屬性就是定義了每個孩子分隔父容器寬度的比例。 比如容器 200px,三個孩子設定了 flex 分別是 1-2-1,那麼他們實際上寬度是 50px 100px 50px。 如果你比較較真,這個flex屬性到底是如何工作的? 實際上他是自動設定了三個屬性:flex-grow, flex-shrink, flex-basis。 而且為什麼強烈推薦用flex,而不是分別設定三個屬性,是因為 flex 屬性會自動計算 flex-basis 屬性的值。 比如容器寬度為 200px,然後你給三個孩子分別設定了 .item1 { flex: 1;} .item2 { flex: 2;} .item3 { flex: 1;} 那麼他們的寬度將是 50-100-50 如果你想單獨設定每一個孩子的三個屬性,你會發現竟然無法按照 1-2-1 的比例平分了。 因為flexbox的計算每個孩子的寬度分兩部分: 1,計算出孩子本身的寬度 2,將剩餘的空白按比例平分給每一個孩子。 所以你如果這樣定義: .item1 { flex-grow: 1;flex-shrink:1;} .item2 { flex-grow: 2;flex-shrink:2;} .item3 { flex-grow: 1;flex-shrink:1;} 你會發現孩子不是 50-100-50 的寬度,因為他們是按照 1-2-1 平分了剩餘的空白。 假設每個孩子本身是 20px 寬度,那麼這麼定義之後,他們其實按 1-2-1 平分了 140px 的空白。加上自身寬度之後比例 顯然不是 1-2-1。 所以你還需要把它們的flex-basis也設定成 1-2-2,這麼寫才行: .item1 { flex-grow: 1;flex-shrink:1;flex-basis: 20px;} .item2 { flex-grow: 2;flex-shrink:2;flex-basis: 40px;} .item3 { flex-grow: 1;flex-shrink:1;flex-basis: 20px;} 而這樣就很麻煩,不如直接用 flex 屬性來定義。而事實上,你可以認為 flex 屬性就是幫你做了上面的計算。 align-self 單獨在當前孩子上覆蓋了 align-items 屬性。