Flex Layout 詳解

2019/5/7號二次更新:爲摺疊的flex item的例子增添了codePen連接。
2019/5/7號更新:補充了總體結構,若有疑問,歡迎留言。
css


總想寫flex,卻一直拖——拖——拖,今天算是寫完了。與以往的文章不一樣,這篇文章更像是寫給本身看的,因此有些術語不會再解釋,如看不懂,能夠先去這篇文章裏看看術語.
html

總體結構

咱們先看這個例子瀏覽器

div.parent {
display: flex;
width: 1000px;
height:400px;
border: solid 2px rgba(75, 234, 12, 0.7);
border-radius: 8px;
}
div.parent > div {
border-radius: 8px;
flex: 1 1 auto;
color: #fff;
text-align: center;
line-height: 200px;
font-size: 26px;
}
div.first-child {
width: 200px;
height: 200px;
background-color: rgb(64, 166, 249);
}
div.second-child {
width: 400px;
height: 200px;
background-color: rgb(255, 141, 75);
}
複製代碼
<div class="parent">
<div class="first-child">first-child</div>
<div class="second-child">second-child</div>
</div>
複製代碼

效果以下:網絡

上面的父級box是個flex container, 有兩個flex item(div.first-child和div.second-child)。 dom

flex direction默認爲row,那麼橫座標軸稱做main axis,flex container(flex item)在main axis方向的尺寸(即寬度)稱做flex container(flex item)的main size, flex container的main size方向的起點叫main start,終點叫main end。post

縱座標軸稱做cross axis,,flex container(flex item)在cross axis方向的尺寸(即高度)稱做flex container(flex item)的cross size。flex container的cross size方向的起點叫cross start, 終點叫cross end。性能

在flex container裏,flex item 會沿着main start依次排列,直至排到main end。接着換行,從cross satrt 開始往cross end方向排列。 字體

總體結構就大體如上,接下來會逐一講解flex container,flex item,以及它們之間的渲染原理。flex

Flex Container

  1. display爲flex的元素會產生一個flex container,該container在正常流內以block-level的形式存在。ui

  2. display爲inline-flex的元素會產生一個flex container,該container在正常流內以inline-level的形式存在。

  3. 每一個flex container都會爲它的內容創建一個flex formatting context,這與block container爲它的內容創建一個block formatting context很相似。可是,flex container並非block container, 諸如:

  • float和clear在flex container裏面無效(也就是flex item即便設置了float和clear也不起做用)

  • vertical-align在flex item上也不起做用

  • flex container的margin不會與它的內容的margin重疊

  • ::first-line和::first-letter僞元素在flex container上不起做用


注意:當元素爲絕對定位元素、浮動元素或根元素時,display屬性值的computed value的計算規則參照下表
Specified value Computed value
inline-table table
inline, table-row-group, table-column, table-column-group, table-header-group, table-footer-group, table-row, table-cell, table-caption, inline-block block
inline-flex flex
others same as specified

Flex Item

簡單的說,flex container的每一個孩子都是一個flex item,而每一個flex item都是一個flex-level box,另外,因爲在計算computed value時flex item會發生塊級化,所以每一個flex item也是一個block container。而對於沒有html元素包裹的連續文字,會被自動包裹在一個匿名的block container flex item裏。

釋1: 塊級化 —— 儘管有的flex item的display屬性值爲inline,可是在計算computed value時,也會被設爲block.

釋2: block container ——該box只能包含block-level box或是創建一個inline formatting context且只能包含inline-level box

1. flex item爲絕對定位元素

由於爲絕對定位元素,因此該flex item會脫離流。效果就如同該flex item的static position box(在計算絕對定位元素位置時,會先假設它的position爲static,處於正常流中,而後得出一個static position,再依據這個static position去定位。)爲flex container的content box。也就是該flex container裏面只有該flex item的static position box,這個flex container是個匿名的flex container。

例:爲div.first-child加上絕對定位。div.first-child改成:

div.first-child {
Width: 200px;
height: 200px;
background-color: rgb(64, 166, 249);
position: absolute;
}
複製代碼

效果以下:

選中div.second-child能夠看到,它的寬度獨佔1000px。

若是給絕對定位flex item加上align-self: center,它會處於它所在的flex container的交叉軸的中心位置。修改div.first-child以下:

div.first-child {
width: 200px;
height: 200px;
background-color: rgb(64, 166, 249);
position: absolute;
align-self: center;
}
複製代碼

效果以下:

而一旦爲絕對定位flex item設置了top/bottom,它就會參照第一個父級定位元素去移動,align-self也就失效了。如今設置div.parent爲相對定位元素,修改爲:

div.parent {
display: flex;
Width: 1000px;
height:400px;
border: solid 2px rgba(75, 234, 12, 0.7);
border-radius: 8px;
position:relative;
}
複製代碼

爲div.first-child設置偏移量,修改以下:

div.first-child {
width: 200px;
height: 200px;
background-color: rgb(64, 166, 249);
position: absolute;
align-self: center;
left: 50px;
top: 50px;
}
複製代碼

效果以下:

對比上圖,能夠看到交叉軸上div.first-child距離div.parent的上邊緣只有上圖的一半,也就是50px,同時div.first-child距離div.parent的左邊緣也是50px。

2. flex item的margin和padding

  • 相鄰的flex item的margin不會重疊(flex container的margin也不會與flex item的margin重疊)。
  • margin和padding若設置爲百分比,則百分比是基於flex container的inline size(若writing mode是horizontal,則inline size爲寬度;若mode是vertical,則inline size 爲高度 )。

例子:
div.parent {
display: flex;
flex-wrap: wrap;
width: 400px;
height: auto;
background-color: rgba(15, 241, 170, 0.42);
border-radius: 8px;
margin-top: 50px;
}
div.parent > div {
border-radius: 8px;
flex: 1 1 auto;
color: #fff;
text-align: center;
line-height: 200px;
font-size: 26px;
}
div.first-child {
width: 200px;
height: 200px;
background-color: rgb(64, 166, 249);
margin-top: 50px;
margin-bottom: 50px;
}
div.second-child {
width: 400px;
height: 200px;
background-color: rgb(255, 141, 75);
margin-top: 50px;
}
複製代碼

效果以下:

能夠看到div.parent距離root box的上邊緣有50px,而div.first-child的上邊緣距離div.parent的上邊緣也有50px,他們並無重疊。div.first-child及div.second-child的margin-top也沒有重疊。

若是div.parent的display爲block,而且div.parent及div.first-child的border寬度均爲0,它和div.first-child的margin-top會發生重疊。修改div.parent以下:

div.parent {
display: block;
width: 400px;
height: auto;
background-color: rgba(15, 241, 170, 0.42);
border-radius: 8px;
margin-top: 50px;
}
複製代碼

效果以下:

鼠標定位到div.parent上,能夠看到它的margin-top和div.first-child的margin-top確實重疊了。同理,div.first-child與div.second-child的margin-top也重疊了。

3. 摺疊的flex item

  1. Flex item能夠設置visibility: collapse,達到摺疊的效果。效果相似於table的行摺疊。
  2. Flex item即便摺疊了,但依然有個隱形的空殼存在,爲的是保證flex container的cross size的穩定。若是flex container僅有一個flex line(每條flex line上按main size方向依次擺放flex item,相似於line box),那麼某個flex item摺疊可能會影響flex container 的main size,可是不會影響flex container的cross size。
  3. 儘管摺疊的flex item不會被渲染,但依然會出如今dom 樹上。爲了計算摺疊的flex item的那個空殼的大小,在dom樹造成的過程當中,flex layout 會先假設全部item都沒有摺疊,而後在由dom樹造成渲染樹的過程當中,會將摺疊的flex item用一個保留了原始的cross size的空殼替換掉。

例子:

div.collapse-example {
  display: flex;
  > ul.nav {
    height: auto;
    margin: 0;
    margin-right: 20px;
    > li {
      margin-bottom: 5px;
      border-radius: 4px;
      text-align: center;
      font-size: 18px;
      display: flex;
      flex-flow: column;
    }
    > li:target,
    > li:hover {
      cursor: pointer;
      > a {
        border-top-left-radius: 8px;
        border-top-right-radius: 8px;
        font-weight: bold;
      }
      > ul {
        border-radius: 8px;
      }
      a {
        color: #343434 !important;
      }
    }
    > li:not(:target):not(:hover) > ul {
      height: 0;
      overflow: hidden;
      visibility: collapse;
    }
  }
  ul {
    padding: 0 10px;
    > li {
      list-style: none;
    }
  }
}
複製代碼
<div class="collapse-example">
        <ul class="nav">
          <li>
            <a href="#">About</a>
            <ul>
              <li>
                <a href="#">introduction</a>
              </li>
              <li>
                <a href="#">blog</a>
              </li>
            </ul>
          </li>
          <li>
            <a href="#">Product</a>
            <ul>
              <li>
                <a href="#">book</a>
              </li>
              <li>
                <a href="#">beautiful-closes</a>
              </li>
            </ul>
          </li>
          <li>
            <a href="#">Contact</a>
            <ul>
              <li>
                <a href="#">email</a>
              </li>
              <li>
                <a href="#">weixin</a>
              </li>
            </ul>
          </li>
        </ul>
        <article id="main">
            This is an Example 
        </article>
      </div>
複製代碼

效果以下:

鼠標移到菜單上,子菜單會顯示出來。( 點擊連接看看

4. flex item的automatic minimum size

min-width和min-height有了一個新的初始化值,即auto,表示自動設置的min size。

對於flex item,當min-width或min-height的specified value設置爲auto,相應的used value是基於內容來設置的。這個值怎麼定呢?

對於min-width,通常,取主軸上width的specified value與主軸上min-content size(一個box的最小size,該size不會致使overflow)的較小值; 若是主軸上width沒有specified value, 可是有個ratio且交叉軸上width設置了specified value,則根據ratio及交叉軸width的specified value得出一個轉換後的size,再取這個size和主軸上min-content size的較小值;若是既沒有設定主軸width的specified value有沒有ratio,那麼直接取主軸上min-content size。

同理min-height。

一般,automatic minimum size取值合理,可是若是是如下狀況,仍是爲min size設置一個具體的值比較好。

  1. 當flex item的content是一篇較大的文檔,給min-width或min-height設置成字體相關的值比較好,例如min-width: 12em。使用 automatic minimum size可能會致使溢出。
  2. 當flex item有較多的孩子節點時,automatic minimum size的取值須要基於min-content size, 而 layout engine爲了找到min-content size須要一一遍歷全部的孩子節點,有損性能。若是直接給min size設置specified value,就沒必要去找min-content size了。
注意:ratio表示一個元素的width與height的比例關係,通常經過給padding-top設定百分比來表達一個固定比例。(由於padding的百分比值是參考containing box的寬度的)
aspect ratio padding-top value
1:1 100%
16:9 56.25%
4:3 75%
3:2 66.66%
8:5 62.5%

定向和排序

flex container的內容能夠按照必定的方向和順序排列。主要依靠flex container的flex-direction和flex-wrap以及flex item的order。

可是有點需切記,不可依靠flex-direction/flex-wrap 的-reverse值 以及order來代替flex item的本來順序,會影響頁面的可訪問性。

1. flex-direction

Name: flex-direction
Value: row | row-reverse | column | column-reverse
Initial: row
Applies to: flex containers
Inherited: no
Percentages: n/a

2. flex-wrap

Name: flex-wrap
Value: nowrap | wrap | wrap-reverse
Initial: nowrap
Applies to: flex containers
Inherited: no
Percentages: n/a

flex: nowrap表示flex container爲單行,內容大小超出則調整flex item的大小以避免溢出。

flex: wrap 表示在內容過多時flex container會換行

flex: wrap-reverse 表示在內容過多時flex container會換行,但cross-start及cross-end方向調換。

3. Order

Name: order
Value:
Initial: 0
Applies to: flex items
Inherited: no
Percentages: n/a

order能夠設置flex item的位置。 這些flex item會在文檔順序的基礎上再基於order屬性值進行再排序,而後按照再排序的結果渲染出來。若是某些flex item的order值相同,則按照它們在文檔裏的順序渲染。

例子:
div.parent {
display: flex;
width: 400px;
height: auto;
border: solid 2px rgba(15, 241, 170, 0.42);
border-radius: 8px;
}
div.parent > div {
border-radius: 8px;
flex: 1 1 auto;
color: #fff;
text-align: center;
line-height: 200px;
font-size: 26px;
}
div.first-child {
width: 200px;
height: 200px;
background-color: rgb(64, 166, 249);
}
div.second-child {
width: 400px;
height: 200px;
background-color: rgb(255, 141, 75);
order: -1;
}
複製代碼

效果以下:

靈活性

1. flex-grow

當一個flex line還有多餘的空間可擴展,這些空間如何分配給該行的flex item呢?用flex-grow, 它用來設置某個flex item可擴展多少比例的多餘空間。取值爲number,默認爲0。

2. flex-shrink

當一個flex line的空間不足,這些缺失的空間如何分擔給該行的flex item呢?用flex-shrink,它表示某個flex item須要縮小多少比例的空間,取值爲number,默認爲1。

3. flex-basic

定義了分配多餘空間(可爲正數亦可爲負數)前該flex item所佔的main size,瀏覽器會根據該值來計算主軸上的多餘空間。可設定以下屬性值:

  • auto: 當specified value爲auto時,按以下步驟取值。 第一步:採用的used value爲flex item的main size(即主軸上的width或height的屬性值), 第二步:若是這個used value依然爲auto,那麼used value 會基於 flex item的content 獲得一個具體值。
  • content: 基於flex item的content獲得一個automatic size。
  • <width>: 和width或height的設置方式同樣,可設爲<length> 或 <percentage>

默認值爲auto。當主軸爲水平方向,設置了flex-basic,則flex item的width值會失效。例如,若是某個flex item的flex-basic設爲0,則把該flex item的寬度視爲0,即便它自己width爲100px,這個100px也會被歸入多餘空間中,供flex inline的全部flex item一塊兒分配。

4. flex

flex屬性爲flex-grow flex-shrink flex-basic的縮寫,默認值爲0 1 auto,可設定的值爲:

  • initial: 0 1 auto,當有多餘空間時,沒有彈性;當空間不足時,flex item能夠縮小。
  • auto: 1 1 auto,flex會有充足的彈性
  • none: 0 0 auto, flex會徹底沒有彈性
  • <positive-number>: <positive-number> 1 auto
  • <flex-grow> <flex-shrink> <flex-basic>: 若flex-grow省略了,默認值取1;若flex-shrink省略了,默認值取1;若flex-basic省略了,默認值取0。

一般,flex item不會縮小到比min content sizes還要小。爲防萬一,咱們能夠爲flex item設置min-width或min-height。


對齊

1. 使用auto margin對齊

  • 若是設置了flex-grow,auto margin 會被設置爲0;若設置了flex-basis,沒有設置flex-grow,那麼在主軸方向上,根據flex-basis計算出的多餘的空間都會分配給auto margin
  • auto margin的優先級高於justify-content及align-self,軸上任何多餘的空間(不包括負的空間)都會分配給auto margin。
  • 若是box在某個軸上發生溢出了,則auto margin會被忽略,且box在該軸的尾部溢出。(若是既有auto margin, 又有justify-content/align-self/align-items,依然是忽略justify-content/align-self/align-items,採用auto margin,只不過此時由於overflow,接着會忽略auto margin, 在軸的尾部溢出)

總的來講,當存在auto margin時,又有多餘的空間(不包括負的空間),則優先級以下:

flex-grow > auto margin > justify-content/align-self/align-items

若是box在軸上只剩下負的空間(即溢出),則auto margin被忽略。

例子:
div.parent {
display: flex;
flex-wrap: nowrap;
justify-content: center;
width: 1000px;
height: auto;
border: solid 5px rgba(15, 241, 170, 0.42);
border-radius: 8px;
}
div.parent > div {
border-radius: 8px;
flex: 1 1 auto;
color: #fff;
text-align: center;
line-height: 200px;
font-size: 26px;
}
div.first-child {
width: 200px;
height: 200px;
background-color: rgb(64, 166, 249);
margin-left: auto;
}
div.second-child {
width: 400px;
height: 200px;
background-color: rgb(255, 141, 75);
}
複製代碼

如今div.parent的justify-content設爲center, div.first-child及div.second-child設置了flex: 1 1 auto(其中flex-grow: 1, flex-shrink: 1, flex-basis: auto); 同時div.first-child設置了margin-left爲auto,效果以下:

能夠看到div.first-child及div.second-child均擴展了,justify-content及auto margin沒起做用。

如今設置div.first-child及div.second-child的flex爲0 1 auto,修改以下:

div.parent > div {
border-radius: 8px;
flex: 0 1 auto;
color: #fff;
text-align: center;
line-height: 200px;
font-size: 26px;
}
複製代碼

效果以下:

能夠看到,剩餘空間都被div.first-child的auto margin-left 佔據了。

如今去掉div.first的auto margin,修改以下:

div.first-child {
width: 200px;
height: 200px;
background-color: rgb(64, 166, 249);
}
複製代碼

效果以下:

如今,justify-content起做用了。

2.justify-content對齊

justify-content用來設置flex-item在主軸上的對齊方式。

Name: justify-content
Value: flex-start | flex-end | center | space-between | space-around
Initial: flex-start
Applies to: flex containers
Inherited: no

效果以下:

3. align-items和align-self

align-items定義了flex container中flex-item在交叉軸上的對齊方式,有點相似justify-content,是針對全部flex item。

align-self定義了某個flex item在交叉軸上的對齊方式,會覆蓋align-items的值。

Name: align-items
Value: flex-start | flex-end | center | baseline | stretch
Initial: stretch
Applies to: flex containers
Inherited: no

Name: align-self
Value: auto | flex-start | flex-end | center | baseline | stretch
Initial: auto
Applies to: flex items
Inherited: no

  • auto: 對於flex-item,能夠設置align-self爲auto, auto值的computed value會設置爲flex container的align-items的值。
  • center: flex item的margin box 在交叉軸方向上處於flex line的中間位置。若是flex line的高度比flex item的還要低,則flex item在交叉軸的首尾兩端溢出相等部分。
  • baseline: flex item會依照flex line的baseline對齊。在交叉軸方向上,baseline與flex item的margin box的上邊緣距離最遠的那個flex item會被放置在交叉軸的開始位置。
  • stretch: 若flex item的 cross size的computed value爲auto(若是flex-direction爲row,那麼cross size就是flex item的height),且交叉軸方向的margin均不爲auto,那麼align-items/align-self爲stretch時,flex item會被拉伸。flex item的cross size的used value會盡量的接近flex line的高度,若是flex item設置了min(max)-width(height),那麼這個used value會受它們的限制。
效果以下圖:

4. align-contents

Name: align-content
Value: flex-start | flex-end | center | space-between | space-around | stretch
Initial: stretch
Applies to: multi-line flex containers
Inherited: no

align-content定義了flex line在flex container的交叉軸方向上的對齊方式,相似於justify-content,都屬於flex container屬性。只不過它要對齊的對象是flex line,且在交叉軸方向,而justify-content要對齊的對象是flex item,在主軸上。

屬性值以下:

space-around: flex container中的flex line之間的空間相等,第一個flex line與 flex container的margin box的上邊緣之間的空間只有flex line之間的空間的一半。

效果以下:

註明:本篇文章中少數圖片來源網絡,若有侵權,請當即聯繫本人刪除。
相關文章
相關標籤/搜索