CSS Grid | 剛學會怎麼用 Grid,那就來畫個 TV 檢驗圖練練手吧!

畫個飛利浦 PM5544 檢驗圖

Leo Chiu
手寫筆記
9 min readApr 10, 2020

--

飛利浦 PM5544 檢驗圖

為什麼要畫檢驗圖?

昨晚月黑風高,跟幾個夥伴待在實驗室,當時覺得有點悶,不太想做研究。無奈下,想找點其他事情做,突然想到之前在 Codepen 上看過一個專案,是用純 CSS 畫出 TV 的檢驗圖,看起來很厲害!

專案連結: 404 — Video Test Card in pure CSS

但是,這個專案一直都在計算數學,在 CSS 中使用 linear-gradient 畫出檢驗圖中的每個格子,看了都昏了。像這個專案的作者用計算的方式畫圖,如果讓我來做,肯定做到一半就放棄了。

好吧!但是我還是想做怎麼辦呢!突然間靈機一動,想到這個布局有點像棋盤,也有像稿紙,也像一塊塊綠豆糕。此時,你應該知道要用什麼了吧!沒錯,就是專門拿來畫像是綠豆糕這種二維排版的 CSS Grid。

因為我才剛學會怎麼用,還不熟悉怎麼使用 CSS Grid,那不如就來做一下 side project 熟悉一下好了。為了減少摩擦,增進和諧,我們必須耐得住性子,學習培養雅量。哦!不是!是學習新技術。

為什麼是 CSS Grid?

從前從前,在黑暗時期聽說會用 float 排版,到了封建時代 Flexbox 稱霸天下,而城邦時代 Grid 終於出現了。黑暗時間大概是許多人的夢靨,現在也已經不太用 float 排版,取而代之的是 Flexbox。Flexbox 在網頁元件的布局上非常地強大,已經能夠滿足大部分網站切版的需求。

你一定想說:「既生 Flexbox,何生 Grid?」

先說,Grid 並非要用來取代 Flexbox,而是可以用來補足 Flexbox,Grid 是專門用來對付像是綠豆糕二維版型的優良武器。大家對以下的版面絕不陌生,一個網頁通常會被切分成多個區塊,每個區塊各自為政,佔有版面的一部分。

https://www.w3schools.com/css/css_grid.asp

如果用 Flexbox 的話,你應該會想說 flex-directionrowcolumn 交互使用,應該也可以很輕易地解決這個問題吧!沒錯,也是可以做到。但是,如果有人叫你幫他撐 10 秒,他要使用更快的招式,你還會刻意叫他不要用嗎?

迷之音:快還要更快 ⚔。

Grid 在二維排版上,比起 Flexbox 的確是方便不少,只需要簡單的設定便可以達成複雜的事情。Grid 就像是一開始先劃分好所有的領土,你再叫各個領主個去佔領土地。而 Flexbox 像是需要將土地先切分成不少層級後,再慢慢的把訊息傳遞下去,跟每一個層級的領主說你們應該要這樣佔領每個層級的土地。

Grid 怎麼用呢?

前面一直說 Grid 很強大,實際上怎麼使用呢?就讓我們來看一下吧!

👆 用 Display 告訴 Children 使用 Grid 排版

跟在用 Flexbox 時一樣,如果要使用 Grid 排版,必須要在 Parent 上宣告 display: grid

display: grid;

✌️ 在 Parent 宣告 Grid 所有板塊的大小以及位置

在 Parent 宣告 display: grid 後,接著必須宣告版面的大小,分成以下兩種:

  • grid-template-rows:指定每一列的寬度。
  • grid-template-columns:指定每一行的寬度。

以下的範例為:

  • 建立 3 列 (row),分別為 1rem、2rem 與 4rem 的長度。
  • 建立 4 行,皆為 2 rem 的寬度。
grid-template-rows: 1rem 2rem 4rem;
grid-template-columns: repeat(4, 2rem);
gap: 2px;

還有一個 gap ,也就是每個區域之間的距離,而在範例中為每個區塊都相隔 2px。

就像是人跟人之間的「社交距離」,防疫期間相隔室內1.5 公尺室外1公尺。

👌 在所有的 Children 上指定要放在哪個位置

在 Parent 都設定完後,接著就可以在 Child 講說區塊要定位在哪個位置,通常會這樣寫:

grid-row: <起始row-num> / <結束row-num>。
grid-column: <起始column-num> / <結束column-num>。

所以,以下的範例便是宣告 Child 會從 row 1 到 row 3,以及從 column 2 到最後一個 column。

grid-row: 1 / 3;
grid-column: 2 / -1;

大致上,只要學到這邊就已經可以畫出 TV 檢驗圖,剩下還有一些好用的 Grid 方法,像是 max-contentmin-contentminmax()auto-fit 等,但是這次畫圖用不到,以後有機會再討論。

耐心是通往成功的道路

講到這邊,你大概已經知道,要畫出 TV 檢驗圖只是耐心而已。你需要的是 Grid 的基本知識,以及一部電視劇或是 Twitch。接下來來說說畫圖的心路歷程吧!

使用 Vue 畫重複的物件

因為在畫 TV 檢驗圖時,經常需要重複畫同樣的物件,所以需要請到爪哇腳本 JavaScript 的幫忙建立大量的 Tag。我不想用香草 JS,也覺得平時喜愛用的 React 不太順,所以最後選擇用 Vue,簡易的用 v-forv-bind:class 幫助我達到目的。

灰色格子為 13 x 23 個

起手式便是宣告 13 x 23 = 299 個灰色背景格子,每隔都為 2rem,並且彼此之間間隔為 1px。

display: grid;
grid-template-rows: repeat(13, 2rem);
grid-template-columns: repeat(23, 2rem);
gap: 1px;

角落的四個圓圈

由於先前定義了 13 x 23 個格子,所以我們可以用這些格子定位角落的四個圓圈。以左上角的圓圈為例,圓圈皆佔 4 個格子,所以,可以用以下的方式定位:

grid-row: 1 / span 4;
grid-column: 1 / span 4;

前面有說到,以 grid-row 來說,前後兩項分別為 <start row-num><end row-num> ,而第二項可以用 span 來指定相對擴展幾格,例如: 1 / span 4 即是從 row 1 往後擴展 4 格。

如果把圓圈定位在 4 x 4 的網格中間,殺雞焉用牛刀,小部局使用 Flexbox 更為方便:

display: flex;
justify-content: center;
align-items: center;

然後在圓圈中,又可以定義 2 x 2 的網格,放 4 個黑色的方塊。

display: grid;
grid-template-rows: repeat(2, 2rem);
grid-template-columns: repeat(2, 2rem);
gap: 1px;

所以,小圓圈的部局依序是 Grid → Flexbox → Grid,滿足了前面說的:「Gird 可以被用來補足 Flexbox 不好用的部分」,像是你要定位四個在角落的小圓圈,如果用 Flexbox 排版,可能會用到像是 position 等等的指令把元素塞到各個角落,但是 Grid 卻可以一氣呵成讓你快速完成二維排版。

定位中間的大圓圈

與四周角落的圓圈定位方法差不多,也是用 Grid →Flexbox →Grid 的方式將大圓圈方到正中央的位置。

然後,可以用 overflow: hidden 把超出圓圈的部芬都隱藏起來。

大圓圈的內容物

大圓圈中是用 Grid 定位 10 列 (row)。這個我覺得是最麻煩的部分,原因是 13 x 23 格灰色格子都設定了 1px 的 gap,因此,這 10 列應該都有間距 1 px,問題就在這邊,如果用 gap 定義了 1px 的間距,每一列之間將會有白色的間距,這樣會與原圖不符。

所以,如果要符合原圖,最好的方法是每列都直接定義合適的寬度

display: grid;
grid-template-rows: calc(1rem + 1px) repeat(3, calc(2rem + 1px)) calc(4rem + 2px) 2rem calc(4rem + 2px) repeat(2, calc(2rem + 1px)) calc(3rem + 2px);
grid-template-columns: calc(24rem + 12px);

目前只想得到這種有點醜的方法,如果有更好的方法,再麻煩告訴我 😅

這個圓圈的內容物還用到了一點小技巧,如果要把所有內容物都水平置中,可以用與 Flexbox 一樣的方法 justify-content: center ,這個方法可以把所有的 Grid Cell 裡面的物件都置中。

MHZ 的頻率紋路

在 TV 檢驗圖的中下方,原始是用 MHZ 定義每條的紋路,可是因為我不知道怎麼計算,所以先做成看起來像就好 😅。

做法是畫了 N 條的 <div> 直線,也是用 Grid 的方法與 SCSS @for 搭配使用,如果沒有 @for,要用 Grid 做到還真的有點麻煩。

總結一下

畫檢驗圖真的需要很多耐心,畫完以後都不知道已經看了幾集的連續劇,沒有連續劇真的畫不下去 😅。但是也透過了這次的機會,了解了 Grid 的基本使用方法, 算的上是一個入門不錯的 side project (?)。

分享就到這邊,如果喜歡我的文章可以幫我拍個幾下手,在閱讀文章時如果有遇到什麼問題,或是有什麼建議,都歡迎留言告訴我,謝謝。 😃

--

--

Leo Chiu
手寫筆記

每天進步一點點,在終點遇見更好的自己。 Instragram 小帳:@leo.web.dev