對Flex佈局的總結與思考

閱讀本文之前最好對flex佈局有基本瞭解,可以通過“參考資料”中列舉的資源來學習。

flex佈局規範的設計目標

  1. 一維佈局模型(one-dimensional layout model),元素項沿着水平或垂直方向來排列,就像一條沿着一個方向的“流”

    與之對應的,CSS Grid Layout是一個二維佈局模型。兩者互爲補充。
  2. 空間分配(space distribution),(假設主軸是水平方向)元素項的最終寬度受到當前行剩餘空間(或不足空間)的影響,就像是有彈性一樣會膨脹和收縮。
  3. 強大的對齊支持(align and justify),align和justify本質上來說,是定義多餘(空白)的空間要放在哪裏。align(align-items, align-content)定義了交叉軸方向上的多餘(空白)空間分佈,而justify(justify-content)定義了主軸方向的多餘(空白)空間要分佈。
爲了方便討論,我們假設主軸是水平方向,當主軸是垂直方向的時候是同理的。

對align和justify的思考

主軸方向的多餘空間

justify-content定義的是主軸方向的多餘空間要如何分佈
主軸方向的多餘空間的出現是因爲容器寬度 > 元素項寬度之和。如圖:

這個可交互實例來自MDN

等一下,不是說 【主軸方向的多餘空間會分配給元素項->使元素項膨脹->元素項佔滿主軸的空間】 的嗎?爲什麼這裏又有多餘的空間來給justify-content分發呢?這是因爲元素項不一定會膨脹(flex-frow的默認值爲0,默認不膨脹),即使膨脹,膨脹後的寬度也會受到max-width的約束。因此有很多時候,主軸在元素項膨脹以後還是有多餘空間的。

一個行內,交叉軸方向的多餘空間

align-items定義的是一個行內,交叉軸方向的多餘空間要如何分佈

一個行內,交叉軸方向的多餘空間的出現是因爲行的高度大於項的高度。由於各個項的高度不一致,比較高的項會將整行的高度撐開,對於那些比較矮的項,在它的垂直方向上就會出現多餘空間。如下圖:

這個可交互實例來自MDN

關於高度撐開的討論,見用css控制元素高度:自底向上和自頂向下的方法

行與行之間,交叉軸方向的多餘空間

align-content定義的是行與行之間,交叉軸方向的多餘空間要如何分佈

在這裏說的“行”,指的是一個flex容器內,由於flex-wrap: wrap造成的換行(下面會討論到換行),而不是指【第一個flex容器是一行,第二個flex容器是第二行】!

行與行之間,交叉軸方向的多餘空間的出現是因爲容器高度 > 容器內各行的高度之和
前面說過,一個行的高度是由這一行中最高的項撐開的。一個flex容器,默認的時候(height:auto),其高度也是被其內部的所有行的高度撐開的,在這個時候容器的高度恰好等於所有行的高度之和,不存在“行與行之間,交叉軸方向的多餘空間”。
但是如果容器本身定義了height: 10000px呢?它的高度就固定了,不會受到其內部的行的影響。這時候,如果所有行的高度之和不足以填滿容器高度,交叉軸方向就會出現多餘空間。如圖:

這個可交互實例來自MDN

對空間分配的思考

flex是如何計算項的寬度的?

所有項按照原始寬度在容器中排列。

原始寬度由flex-basis決定,由於flex-basis默認值是auto(意思是取content-box的寬度作爲flex-basis),因此一般width就是原始寬度。如果沒有定義width,則width由項的子元素撐開。

如果容器有多餘的寬度,則將這些多餘寬度分配給每個項(分配比例由flex-grow控制),使得項的寬度增加,得到每個項的flex寬度最終寬度基於flex寬度,但還會受到min-width、max-width的限制。
如果所有項的原始寬度已經超過了容器元素的寬度,那麼會先檢查flex-wrap是否允許換行,如果允許換行,則換行以後再計算flex寬度;如果不允許換行,則將超出的寬度分配給每個項(分配比例由flex-shrink控制),使得項的寬度減小,得到每個子元素的flex寬度最終寬度基於flex寬度,但還會受到min-width、max-width的限制。

總結來說就是,width決定原始寬度,flex-grow/flex-shrink決定分配比例,min-width、max-width限制最終寬度

flex寬度計算的例子(可在瀏覽器中打開):

<!DOCTYPE html>
<html lang="en">

<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <meta http-equiv="X-UA-Compatible" content="ie=edge">
  <title>Document</title>
</head>

<body>
  <div class="container">
    <div class="flex">
      <div class="content1">
        <div class="inner1"></div>
      </div>
      <div class="content2">
        <div class="inner2"></div>
      </div>
    </div>
  </div>
</body>

<style>
  .flex {
    display: flex;
    width: 1000px;
    /* 容器元素的剩餘剩餘寬度將被分配 */
  }

  .content1 {
    flex-grow: 1;
    height: 100px;
    background-color: chocolate;
    /* 沒有定義width,則由它的子元素撐開 */
  }

  .content2 {
    flex-grow: 1;
    height: 100px;
    background-color: aqua;
    /* 沒有定義width,則由它的子元素撐開 */
  }

  .inner1 {
    height: 50px;
    background-color: cornflowerblue;
    /* 將父元素的寬度撐開爲100px */
    width: 100px;
  }

  .inner2 {
    height: 50px;
    background-color: greenyellow;
    /* width設置爲百分比時,無法撐開父元素,因此父元素的原始寬度爲0 */
    width: 100%;
  }
</style>

</html>

計算過程:
content1原始寬度100px,content2原始寬度0px。剩餘寬度爲1000px-100px=900px。
由於content的flex-grow都相等,因此剩餘寬度被平均分配,每個content分到450px。
content1最終寬度100px+450px=550px,content2最終寬度0px+450px=450px。

佈局的一般流程

  1. 根據UI設計,確定需要多少“行”來顯示所有內容,然後確定每一“行”有哪些“項”一個“項”本身也可以成爲容器,包含一行或多行。

    這裏的“行”指的就是一個flex-direction:row容器了(與之前的討論不同)。它可以設置flex-wrap:wrap,使得一個“行”容器在寬度不夠容納子元素的時候,在容器內部產生換行。
  2. 對每一“行”,定義其樣式。行是一個flex容器(display:flex)。並使用justify-content、align-items來定義元素在容器中的分佈方式。通過margin-top來定義行之間的縱向距離
  3. 對每一“項”,定義其樣式。使用margin、padding來對元素位置進行微調。合理使用flex-grow、flex-shrink、width、min-width、max-width來調整元素的寬度。通過margin-left來定義元素之間的橫向距離。如果這“項”本身也是一個容器(包含一行或多行),返回第2步。

參考資料

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章