關於 Grid 佈局的那點事兒

西流河發表於2018-04-29

相信大部分前端小夥伴已經有過使用 Grid 佈局的體驗,一定是美滋滋。Flex 佈局和 Grid 佈局作為 CSS 佈局領域的兩大新星,可謂是擁護者甚眾。我在前面這篇文章中總結了 Flex 佈局的強大之處,今天再來總結一下 Grid 佈局的基礎知識。

基本介紹

Grid 佈局在 2017 年開始已經逐漸獲得各大主流瀏覽器的支援,雖然暫時無法在生產環境中運用,但是它作為一項令所有 UI 工程師歡欣鼓舞的技術,是值得馬上去了解它的奧妙的。為了獲得更好的除錯體驗,我們可以在 FireFox 瀏覽器中學習它(FireFox 的開發者工具對它的支援很友好)。

那麼,它與傳統的佈局方案,包括前面介紹的 Flex 佈局方案相比的特性在於:

  • 它是第一個真正意義上的佈局系統,其主要表現在它是第一個基於二維方向的佈局模組
  • 它是第一個基於網格(或者叫柵格,本文叫網格)的原生布局系統

所謂基於二維方向,是指它可以針對水平和垂直方向兩個維度上進行佈局設計。在它之前,我們的佈局方案,只能在水平方向上進行佈局,對於垂直方向則無能為力,而 Grid 則可以輕鬆地實現二維佈局。

那麼,什麼是網格呢?我們可以這樣理解,網格是由一組水平和垂直的直線交叉,形成一個個矩形區域,我們可以將元素放置在這些區域中,這樣的好處是,元素可以很好地控制和對齊。

可以說,我們以前採取的佈局方案,其實是工程師們沒法子想出來的辦法,畢竟 position,display 等屬性,並不是為了佈局而生的。而 CSS Grid,才是一個真正的佈局系統。

核心概念

瞭解了 CSS Grid 佈局的優越性,我們馬上來了解如何運用這項技術。

和 Flex 類似,Grid 的使用同樣簡單,第一步,我們需要把某個容器指定成網格容器:

.grid {
    display: grid || inline-grid;
}
複製程式碼

這個時候,.grid 就變成了一個 網格容器(Grid Conatainer),包含在這個容器中的子元素則自動變成了 網格項(Grid Items), Grid 的所有屬性都在兩個概念之間展開。

下面這張圖,就是一個網格容器中,包含著 9 個網格項。

Grid 示意

上面這個等寬的三行三列的佈局,如何實現呢?在傳統方案中,我們可能需要藉助浮動屬性等來實現,但是藉助 Grid ,就很簡單了,在為父元素指定 display: grid; 之後,我們可以藉助兩個屬性: grid-template-columns, grid-template-rows 和一個新單位 fr 來輕鬆實現。

.grid {
    display: grid; 
    grid-template-columns: 1fr 1fr 1fr;
    grid-template-rows: 1fr 1fr 1fr;
    grid-gap: 20px 10px;
}
複製程式碼

grid-template-columnsgrid-template-rows 分別表示水平方向上和垂直方向上網格項的空間分配比例,fr 是一個新單位,表示佔據可用空間的一等分。所以上面的程式碼表示,在水平和垂直方向分別把可用空間分為三份,且三份佔據空間相等。grid-gap 屬性指的是網格項之間的間隙,後面會介紹到。

學習到了這兩個屬性,我們已經可以輕鬆地佈局出常見的多欄佈局,例如我們要實現一個水平為 1:2:1 的佈局,可以這樣實現:

.grid {
    display: grid;
    grid-template-columns: 1fr 2fr 1fr;
    grid-gap: 10px;
}
複製程式碼

grid 實現 1-2-1 三欄佈局

當然我們同樣可以為上面的屬性設定固定的長度,例如畫素值。

Grid 佈局為我們提供一個 repeat() 函式來更高效地實現複雜的佈局,它可以接收兩個值,第一個值是重複的數量,第二個值是重複的模式,例如:repeat(3, 1fr) 表示三等分的佈局,repeat(3, 1fr 200px)表示把1fr 加一個200px的佈局重複三次。

以上的賦值形式可以混雜來使用,例如:

grid-template-columns: 200px 1fr repeat(3, 100px);
複製程式碼

上面的佈局從左到右呈現為:200px 1fr 100px 100px 100px.

grid-auto-columns grid-auto-rows

在我們沒有顯示地藉助前面的 grid-template-columnsgrid-template-rows 來指定網格項的空間分配方式的時候,我們可以使用 grid-auto-columnsgrid-auto-rows 兩個屬性來設定網格區域的自動顯示範圍,經常可以藉助一個工具函式 minmax() 來指定,例如 grid-auto-rows: minmax(120px, auto),表示不管網格區域中有沒有內容,其高度最小為 120px,如果其有超過 120px 的部分的內容,則自動擴張。

網格線(Grid Line)

所謂網格線,就是兩個網格軌道之間以及網格邊緣的直線,在上面這個三行三列的網格中,水平和豎直方向上各有四條網格線,在 Grid 中,我們通常按照書寫方向,為這些網格線編號,水平方向上,我們從左至右,將它們編號為 “1, 2, 3, 4···”,豎直方向上同理,從上往下編號,如下圖所示:

網格線

網格軌道(Grid Track)

網格軌道指的是任意兩條網格線之間的空間(這條線不一定看得見),在上面的圖片中,我們可以清晰地看到水平和豎直方向上黑線之間的網格軌道,而我們的網格項就是在這些軌道上安放。

網格區域(Grid Area)

網格區域,就是若干個相鄰的網格單元組成的區域,我們可以通過組合網格單元來建立出不同大小的網格區域。

下面為了不致混亂,我們分網格容器和網格項來說明它們上面可以設定的屬性。

網格容器上可以設定的屬性有(因為二維佈局,基本來說,某一個針對水平方向的屬性,同樣有一個數值方向的屬性與之相對應,為了方便,我們一起介紹它們):

  1. display: grid || inline-grid || subgrid
  2. grid-template-columnsgrid-template-rows
  3. grid-auto-columnsgrid-auto-rows
  4. grid-auto-flow
  5. grid-column-gapgrid-row-gap 以及兩者合寫 grid-gap
  6. justify-items
  7. align-items
  8. justify-content
  9. align-content
  10. grid-template-area

網格項上面可以設定的屬性有:

  1. grid-column-start
  2. grid-column-end
  3. grid-row-start
  4. grid-row-end
  5. grid-column(1 和 2 的合寫形式)
  6. gird-row(3 和 4 的合寫形式)
  7. grid-area
  8. justify-self
  9. align-self

別看上面的屬性很多,其實讀者完全不必慌,一則是這些屬性中高頻使用的就幾個,二者是上面好幾組屬性完全可以用它們的縮寫形式來替代。下面來介紹其中高頻使用的一些屬性(更詳細的大家可以在 MDN 上檢視詳情介紹),前面已經瞭解的部分不再贅述。

grid-gap

grid-gap 用來設定網格間距,也就是兩個網格之間留出來的空白,其可以在橫向和縱向分別通過 grid-column-gapgrid-row-gap 來設定相應的大小,這兩個屬性值通常可以合寫為 grid-gap

.grid {
    grid-gap: 20px 10px;
}
複製程式碼

在橫向設定 10px 的間距,縱向設定 20px 的間距,如果,橫向和縱向要設定的大小一致時,可以直接縮寫為一個值: grid-gap: 20px;

我們可以從這張圖上看到這些概念。

網格核心概念

grid-template-areas

這個屬性十分方便,可以通過指定網格單元的名字來定義一個網格模板,然後在網格項上面使用 grid-area 屬性與之配合,來確定其顯示的區域。重複相同的網格單元的名字,就會自動合併兩個單元,可以使用一個句點 . 來表示一個空白的網格單元。例如以下的一個佈局形式:

grid-area

程式碼:

.grid {
    display: grid;
    grid-gap: 10px;
    grid-template-columns: 1fr 1fr 1fr;
    grid-template-areas: "header header header"
                         "siderbar content content"
                         "footer footer footer"
}

.one {
    grid-area: header;
}

.two {
    grid-area: siderbar;
}

.three {
    grid-area: content;
}

.four {
    grid-area: footer;
}
複製程式碼

通過例項我們可以看到,在網格容器上,我們為其定義網格區域的顯示模板,然後在相應的網格項上將設定好的 grid-area-name 賦值給 grid-area 屬性,這樣就可以很便捷地達到我們的效果。

justify-itemsalign-items

我們知道在 Flex 佈局中,可以藉助 justify-content 以及 align-items 來設定彈性專案在水平和豎直方向的對齊方式,強大的 Grid 系統自然也可以進行設定,其屬性和 Flex 很相似,分別使用 justify-itemsalign-items(後者和 Flex 一致有沒有)。不同的是,這兩個屬性的屬性值是參照網格線來確定它們的位置:

.grid {
    justify-items: start | end | center | stretch;
}
複製程式碼

屬性值介紹:

  • stretch: 預設值,內容充滿整個網格區域
  • start:網格項內容與網格區域左側對齊
  • end: 網格項內容與網格區域右側對齊
  • center: 網格項內容在網格區域居中顯示

align-items 與前者相同道理,不再贅述。如果在前面的例子中,我們將這兩個屬性值設定為 center, 則它們就會呈現為下面這幅樣子:

justify-items 和 align-items

gird-columngrid-row

除了在網格容器上統一進行的設定,我們可以針對特定的網格項進行設定,決定其佔據的網格區域,我們可以藉助網格線的約束,來決定某一個網格項的空間。例如,我們想要實現一個元素佔據水平方向從第 1 條網格線到第 3 條網格線,豎直方向從第 2 條網格線到第 4 條網格線,也就是下面紅色元素的位置:

跨軌道放置元素

如何實現呢,水平方向藉助這兩個屬性:grid-column-start, grid-column-end,分別表示該網格項開始和結束的網格線序號,其值是代表網格線的編號。

.item1 {
    grid-column-start: 1;
    grid-column-end: 3
}
複製程式碼

豎直方向同理,使用 grid-row-startgrid-row-end 兩個屬性實現:

.item1 {
    grid-row-start: 2;
    grid-row-end: 4;
}
複製程式碼

如此一來,就可以實現上面圖中的效果,這兩組屬性同樣有相應的縮寫形式,我們把 grid-column-start, grid-column-end 合寫為 grid-column,把 grid-row-startgrid-row-end 合寫為 grid-row,其值用一個 / 來分隔。

grid-column: 1/3;
grid-row: 2/4;
複製程式碼

justify-selfalign-self

這兩個屬性用來設定特定網格專案在其網格區域中的對齊方式,其可取的屬性值與 justify-itemsalign-items 相同。不再說明。

更改重疊區域的層級順序

當兩個網格區域在空間上發生重疊之時,其層疊順序按照其在 DOM 結構中的出現前後決定,後來者會居上。如果我們想人為改變這些重疊次序,可以為相應的區域設定 z-index 屬性,來改變它的層疊優先順序,這和定位元素的設定一致。

以上就是 Grid 佈局的基本概念,我們可以看到,最常用的幾個屬性是:grid-template-* 相關的幾個屬性,用來指定網格區域留白的 grid-gap,以及用來宣告網格專案在各自網格區域中的對齊方式的 align-items 以及 justify-items 兩個屬性。在網格項上,用來指定網格項顯示區域的 grid-column 以及 grid-row 屬性,指定特定網格項在其網格區域中對齊方式的 align-selfjustify-self 屬性。用熟了這些屬性,Grid 佈局的使用自然是手到擒來,至於其它更復雜的屬性,在後來的學習中循序漸進使用並熟悉。文中有什麼疏漏和錯誤之處,希望大家指正。

參考資料

相關文章