為什麼要畫檢驗圖?
昨晚月黑風高,跟幾個夥伴待在實驗室,當時覺得有點悶,不太想做研究。無奈下,想找點其他事情做,突然想到之前在 Codepen 上看過一個專案,是用純 CSS 畫出 TV 的檢驗圖,看起來很厲害!
但是,這個專案一直都在計算數學,在 CSS 中使用 linear-gradient
畫出檢驗圖中的每個格子,看了都昏了。像這個專案的作者用計算的方式畫圖,如果讓我來做,肯定做到一半就放棄了。
好吧!但是我還是想做怎麼辦呢!突然間靈機一動,想到這個布局有點像棋盤,也有像稿紙,也像一塊塊綠豆糕。此時,你應該知道要用什麼了吧!沒錯,就是專門拿來畫像是綠豆糕這種二維排版的 CSS Grid。
因為我才剛學會怎麼用,還不熟悉怎麼使用 CSS Grid,那不如就來做一下 side project 熟悉一下好了。為了減少摩擦,增進和諧,我們必須耐得住性子,學習培養雅量。哦!不是!是學習新技術。
為什麼是 CSS Grid?
從前從前,在黑暗時期聽說會用 float 排版,到了封建時代 Flexbox 稱霸天下,而城邦時代 Grid 終於出現了。黑暗時間大概是許多人的夢靨,現在也已經不太用 float 排版,取而代之的是 Flexbox。Flexbox 在網頁元件的布局上非常地強大,已經能夠滿足大部分網站切版的需求。
你一定想說:「既生 Flexbox,何生 Grid?」
先說,Grid 並非要用來取代 Flexbox,而是可以用來補足 Flexbox,Grid 是專門用來對付像是綠豆糕二維版型的優良武器。大家對以下的版面絕不陌生,一個網頁通常會被切分成多個區塊,每個區塊各自為政,佔有版面的一部分。
如果用 Flexbox 的話,你應該會想說 flex-direction
的 row
與 column
交互使用,應該也可以很輕易地解決這個問題吧!沒錯,也是可以做到。但是,如果有人叫你幫他撐 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-content
、min-content
、minmax()
、auto-fit
等,但是這次畫圖用不到,以後有機會再討論。
耐心是通往成功的道路
講到這邊,你大概已經知道,要畫出 TV 檢驗圖只是耐心而已。你需要的是 Grid 的基本知識,以及一部電視劇或是 Twitch。接下來來說說畫圖的心路歷程吧!
使用 Vue 畫重複的物件
因為在畫 TV 檢驗圖時,經常需要重複畫同樣的物件,所以需要請到爪哇腳本 JavaScript 的幫忙建立大量的 Tag。我不想用香草 JS,也覺得平時喜愛用的 React 不太順,所以最後選擇用 Vue,簡易的用 v-for
與 v-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 (?)。
分享就到這邊,如果喜歡我的文章可以幫我拍個幾下手,在閱讀文章時如果有遇到什麼問題,或是有什麼建議,都歡迎留言告訴我,謝謝。 😃