Grid 佈局發車啦

小烜同學發表於2019-03-01

Grid 佈局又叫做網格佈局,顧名思義是一種基於二維網格的佈局方式,Grid 的強大令人興奮,讓我們一起來走進 Grid 的世界吧。

相容性

好了,大家都坐好,我準備發車啦。不符合要求的同學請下車,沒滿 18 歲的,咳咳,開玩笑。不是所有人現在都能搭上 Grid 的小車車,畢竟專案相容性問題很現實,不過不耽誤我們學習。

相容性

我替大家翻譯一下上面的瀏覽器都是啥,順序從左到右:

  • PC 端: Chrome、Edge、Firefox、IE、Opera、Safari
  • 移動端: Android、Chrome for Android、Edge Mobile、Firefox for Android、Opera、IOS Safari、Samsung Internet

好了,大家看了看瀏覽器的相容性就知道,如今主流瀏覽器都已經支援了 Grid 佈局,還等啥呢?上車吧。

基本概念

乘客們,在開始瞭解用法之前,我們要了解一些 Grid 佈局的基本概念,我想大家應該小時候都用過一種叫小字本的東西,這就是個正兒八經的網格。

小字本
  • Container: 網格容器,當我們設定 display: grid; 就將一個容器變成了網格容器,就比如說上面小字本里外層的那個綠框。
  • Item: 網格項,在我們設定的網格容器中的每一個子元素都是網格項。
  • Line: 網格線,顧名思義啦,這東西就是網格之間分界的線,就上小字本里的橫著豎著的線。
  • Track: 網格軌道,兩條相鄰的網格線之間的空間,也就是網格的行或列。
  • Cell: 網格單元,兩個相鄰的行和列之間的區域,也就像是小字本里的每個小格子了。
  • Area: 網格區域,四條網格線包圍起來的區域。

好了,基本概念瞭解的差不多了,我們去往下一站。

基本用法

第二站到了,我們要繼續吹牛了。

本站要介紹的 api:

  1. grid-template-columnsgrid-template-rows
  2. grid-gap -> grid-row-gap + grid-column-gap

第一件事,掏出程式碼:

<div class="container">
  <div>1</div>
  <div>2</div>
  <div>3</div>
  <div>4</div>
  <div>5</div>
  <div>6</div>
</div>
複製程式碼
body {
  background: #CCCCCC;
}

.container > div {
  font-size: 35px;
  font-weight: bold;
  color: #fff;
  text-align: center;
  background: #666666;
}

.container > div:nth-child(2n) {
  background: #336666;
}

.container > div:nth-child(4n) {
  background: #f37e70;
}

.container {
  display: grid;
  grid-template-columns: 120px 120px 120px;
  grid-template-rows: 50px 50px;
}
複製程式碼

好了,現在它長這個樣子。

Grid 佈局發車啦

我們可以看出上面一些關鍵的 css 程式碼:

  1. 使用 display: grid; 將外層容器變成一個網格佈局容器。
  2. 現在我們擁有了一個容器,我們現在要開始幹什麼了?對,沒錯,我們要開始把這個容器畫成一個一個的格子。
  3. grid-template-columns: 120px 120px 120px; 將容器畫成 3 列,每列 120px;grid-template-rows: 50px 50px; 畫成 2 行,每行 50px。

上面兩個 api 給網格加上了兩條橫線,三條豎線,把容器畫成了一個個的格子。然後將網格項一個一個填進去,那麼聰明的同學又會想了,你這樣畫好格子,裡面有 6 個格子,那我再添一個 div 會出現什麼情況?好吧,滿足這位同學的好奇心,我們加一個 div 進去。

然後就會變成這樣:

Grid 佈局發車啦

神奇,是不是,明明俺就畫了 6 個格子,居然 7 出現了,並且還有一定高度。其實在 grid 裡,它有一個隱式網格軌道。

當我們的網格項處於我們沒有定義的網格部分的時候,它會有一個預設的值,我們也可以選擇去定義隱式網格軌道的大小,通過 grid-auto-rowsgrid-auto-columns 來定義行和列,關於這部分要說的話很多,大家可以到這個部落格去了解。

講完這個,我們再看看,每個格子捱得太近了,一點都不美觀,咋辦呢?我們加上 grid-gap: 2px 4px; 看看:

Grid 佈局發車啦

可以看到,使用這個屬性我們定了網格的間隙,這個 api 其實是兩個 api 的組合(grid-column-gapgrid-row-gap)。

好了,這一站就是基本用法,下面我們繼續發車啦。

fr 單位以及 repeat

上面我們通過一些基礎的屬性,寫了一個 6 個格子的頁面。這一節我們不講屬性,講一下在 grid 中的一個單位值 — fr。那麼這個 fr,代表的是什麼意思呢?在 flex 中也有類似的屬性,fr 的意思就是在自由空間進行分配的一個單位,那麼是什麼意思呢?

比如說,容器寬度為 1000px,現在假如 grid-template-columns: 200px 1fr 1fr 2fr。那麼這就表示分了 4 列,第一列為 200px,然後剩下的 800px 就是自由空間了,經過計算可以得出 1fr 為 200px,這就是 fr 的意義。

那麼,我們上面的例子其實可以這樣寫 grid-template-columns: 1fr 1fr 1fr;。但是現在又出現了一個問題,這個 1fr 寫的好煩,能不能就寫一個。

好訊息,是有的,我們可以使用 repeat 來簡寫,於是上面的例子又可以改成 grid-template-columns: repeat(3, 1fr)

說完這兩個,我們繼續下一站。

網格線的應用

這一站我們要說這些 api:

  1. grid-column -> grid-column-start + grid-column-end
  2. grid-row -> grid-row-start + grid-row-end

好了我們拋棄上面的例子,現在假如我們接到一個需求,我們要使用 3 欄佈局,左右固定 200px,中間自適應,咋辦呢?相信大家一下就想到了,畢竟大家都很聰明。

<div class="container">
  <div>Left</div>
  <div>Main</div>
  <div>Right</div>
</div>
複製程式碼
// ...,跟上面相同的程式碼
.container {
  display: grid;
  grid-template-columns: 200px 1fr 200px;
  grid-template-rows: 500px;
  grid-gap: 2px 4px;
}
複製程式碼

好了,現在我們是這樣的:

Grid 佈局發車啦

需求又來了,我們要加上一個 header 和一個 footer,寬度是 main 的寬度,現在又怎麼辦呢?首先 container 需要改:

.container {
  display: grid;
  grid-template-columns: 200px 1fr 200px;
  grid-template-rows: 100px 500px 100px;
  grid-gap: 2px 4px;
}
複製程式碼

現在我們可以用網格線來進行佈局了,在上面的網格佈局中橫向有 4 條網格線,豎向有 4 條網格線,預設網格會為編號,從 1 開始,於是我們可以用網格線將元素固定到它應該在的位置:

/* 類名就是 html 對應的 div */
.left {
  grid-column: 1 / 2;
}
.main {
  grid-column: 2 / -2;
}
.right {
  grid-column: -2 / -1;
}
.header {
  grid-column: 2 / -2;
}
.footer {
  grid-column: 2 / -2;
}
複製程式碼

這樣的話,我們的佈局就成了這樣子:

Grid 佈局發車啦

在上面有一個小技巧,如果中間線比較多,你要選擇比較靠後的線,就可以使用負數來進行選擇。

上面的 1 和 2 的使用是差不多的,這裡就不詳述了。

注意,你可以給網格線取一些語義化的名字便於你使用,比如下面這樣,footer 一樣可以和 main 等寬:

<div class="container">
  <div class="left">Left</div>
  <div class="main">Main</div>
  <div class="right">Right</div>
  <div class="footer">Footer</div>
</div>
複製程式碼
.container {
  display: grid;
  grid-template-columns: 200px [main-start] 1fr [main-end] 200px;
  grid-template-rows: 200px 200px;
  grid-gap: 2px 4px;
}

.footer {
  grid-column: main-start / main-end;
}
複製程式碼

我們可以給網格線取名字,然後再去使用它。

網格區域應用

這節介紹的 api:

  1. grid-template-areas
  2. grid-area

我們可以通過另一種建立網格的方式來定位元素,就如同畫圖一樣,就那上面那個有 header 和 footer 的例子來說,我們可以這麼寫:

.container {
  display: grid;
  grid-template-columns: 200px  1fr  200px;
  grid-template-rows: 50px 300px 50px;
  grid-template-areas: 
    ". h ."
    "l m r"
    ". f .";
  grid-gap: 2px 4px;
}

.header {
  grid-area: h;
}
.left {
  grid-area: l;
}
.right {
  grid-area: r;
}
.main {
  grid-area: m;
}
.footer {
  grid-area: f;
}
複製程式碼

通過這樣的方式,我們一樣能做到上面的那種定位操作,注意的是在 grid-template-areas 中 . 代表的是這個位置空著。

repeat 進階

在上面,我們說過一種 repeat 的簡單用法,建立網格時重複指定的次數,但是有的時候我們並不想指定次數,而是希望自動填充,這時候怎麼辦呢?

這時候我們就要提到 auto-fitauto-fill 了。

首先,我們通過 repeat 先把格子建出來:

.container {
  display: grid;
  grid-template-columns: repeat(9, 1fr);
  grid-template-rows: 50px;
  grid-gap: 2px 4px;
}
複製程式碼

這樣我們就建立了一個基於 9 列的網格系統,如果我們的視窗不斷變小,那麼我們的每一格也會相應的變窄,我們不希望它變得非常窄,咋辦呢?

Grid 有一個 minmax() 函式可以使用,這個函式接收兩個引數,一個最小值,一個最大值,當瀏覽器視窗發生改變的時候,它能夠保證該元素是在這個範圍之內改變。比如說:

.container{
  grid-template-columns: repeat(9, minmax(250px, 1fr));
}
複製程式碼

當我們把 grid-template-columns 變成這樣之後,每一列的寬度都會在 250px 到 1fr 之間,但是我們會發現,他裝不下這些格子,但是它也沒有換行,因為你告訴它有 9 列,於是出現了滾動條,但是你不希望出現這東西,咋辦呢?

這時候就到了我們上面說的兩個引數出場的時候到了。

.container{
  grid-template-columns: repeat(auto-fill, minmax(250px, 1fr));
}
複製程式碼

當我們加上這個引數過後,就會讓瀏覽器去處理列寬和換行的問題,如果你給的容器寬度不夠,它就會換行。

那麼 fit 和 fill 有啥區別呢?我找了一些資料,裡面有兩句總結是這麼說的:

auto-fill 傾向於容納更多的列,所以如果在滿足寬度限制的前提下還有空間能容納新列,那麼它會暗中建立一些列來填充當前行。即使建立出來的列沒有任何內容,但實際上還是佔據了行的空間。

auto-fit 傾向於使用最少列數佔滿當前行空間,瀏覽器先是和 auto-fill 一樣,暗中建立一些列來填充多出來的行空間,然後坍縮(collapse)這些列以便騰出空間讓其餘列擴張。

做一個實驗,當寬度足夠大時,這兩者區別就出來了:

.container1{
  grid-template-columns: repeat(auto-fill, minmax(40px, 1fr));
}
.container2{
  grid-template-columns: repeat(auto-fit, minmax(40px, 1fr));
}
複製程式碼
Grid 佈局發車啦

這樣可以看出區別了,fill 是儘可能多容納列,它會自己造一些列來填充剩餘空間,其實它是鋪滿了的,只是你看不見而已,而 fit 是擴張原有列來鋪滿這一行。

至於具體詳細的解釋,大家可以去這篇譯文看一看,說得非常詳細了。

總結

好了,關於 grid 大概就說這麼多了,其實關於這些 api 的使用還有很多的引數問題,這裡沒有細說,只是讓大家知道 grid 能幹嘛,至於詳細的說明,可以去下面幾個部落格看一看。

另外還有一些屬性沒有介紹,是關於對齊等屬性,大家在有需求的時候可以去下面的資料找到。

更多資料:

如果各位看官看的還行,可以到 我的部落格倉庫 裡給我一顆小小的 star 支援一下,不勝感激。

相關文章