[12th 鐵人賽] 這不是 Media Query, Day 16

Media Query

這件事情應該是講到爛掉了,我們稍微複習一下就好,

@media (min-width: 32rem) {}

收工(欸)!

爾或者是,

/* 請不要這樣做 */
@media (min-width: 90ch) {}

等等好歹也解釋一下 ch

好的,這邊這個單位 ch 是一種字元長度單位,但是他不代表你的 字有多長,以下是 w3c 的解釋,

Equal to the used advance measure of the "0" (ZERO, U+0030) glyph found in the font used to render it. (The advance measure of a glyph is its advance width or height, whichever is in the inline axis of the element.)

根據 caniuse.com 的回報,普及率也算高。這個單位很適合用來規劃文字內容區塊的排版。然而如果無法正確量測尺寸,這個單位會使用 0.5em 來當作預設值。

通常這個單位會搭配一些運算,並且放入 Flex 或 Grid 當中,最外面的 Media Query 只是輔助。也就是說,我們在面對排版的大前提下,針對「尺寸」這件事情,所配用的順序是這樣,

  • @media 指定各種裝置尺寸所需要的外層容器尺寸。
  • Grid 用以規劃容器內的元件流。
  • Flex 用以規劃元件內更小級別的元件流。

所以,當你不需要針對特殊斷點做設定的話,甚至連 @media 都可以省略。


Flex 與 Grid

我先來兩個比較魔術的寫法,

.grid {
    grid-template-columns: repeat(auto-fit, minmax(15ch, 1fr));
}
.flex-item {
    flex-grow: 1;
    flex-shrink: 1;
    flex-basis: calc((48rem - 100%) * 9999);
}

可以不解釋嗎?

好的,為了防止路上被人賭到,我先解釋一下上面這些個魔術方法在做什麼事情。首先,請先自行複習一下 repeatauto-fit 這兩件事情,後面的 minmax(15ch, 1fr) 我來解釋解釋,

首先,我們的尺寸使用了 minmax 亦即他的尺寸會介於 15ch1fr 之間。我知道這是廢話,然後那個 15ch 是依照字元寬度來設定成 最小 尺寸,接著使用 1fr 來當作最大尺寸,我知道這還是廢話。這樣設定主要的用意就是,

  • 格線容器的內容會自動緊湊符合(auto-fit)。
  • 當格線容器寬度小於 15ch 的時候,會變成只有一個 column,這個 column 的尺寸是 1fr
  • 當格線容器大於 15ch 的時候,會從 15ch 開始分 column 排列,然後自動切斷成 row
  • 當有多欄(column)出現時,每個欄位的寬度都會是 1fr,然後會套用自動緊湊符合(auto-fit)。

所以我們會有一下這些結果,


我們接著來看 Flex 的設定,

.flex-item {
    flex-grow: 1;
    flex-shrink: 1;
    flex-basis: calc((48rem - 100%) * 9999);
}

上面的 flex-grow: 1;, flex-shrink: 1; 就不解釋了。這邊的魔法在於 calc() 這個方法。一般來說,我們在設定 flex-basis 的時候,通常會指定一個數值,或是項這樣透過 calc() 做運算,但是,這邊混合了一個包含單位的絕對數值,與一個相對數值,然後後面還乘上了一個無單位數字。那,到底這樣會發生什麼事情?

  • 當尺寸大於 48rem 的時候,他會變成多欄(column)的形式呈現。
  • 多欄的形式會呈現出不固定的欄位寬度,此時是因為 flex-grow 的關係會將欄位寬度撐滿容器。
  • 如果尺寸小於 48rem 的時候,他會變成單欄多行(row)的形式。
  • 這個時候的欄位寬度,會因為 flex-grow 的關係會將欄位寬度撐滿容器。
  • 後面那個 * 9999 是為了避免中間值誤差。

所以,我們會有以下兩種結果,

何謂 中間值誤差,我們看圖,

為何會稱他為誤差,請回頭看一下 Grid 那邊的操作,照理來說,我們會希望 1, 2, 3, 4 這四個元件的尺寸會相同,頂多就是因為寬度不夠而被換行,但是,在 Flex 的呈現,卻變成了 4 填滿了整個容器。所以,為了避免這種偏差值存在,所以我們後面再加上 * 9999 這樣的設定。

我再解釋一次 calc()48rem - 100% 這邊的操作,

  • 當容器尺寸為 900px,是大於 48rem 的狀況(假設 1rem = 16px),運算結果為:-132 然後乘上 9999 變成了:-1319868px
  • calc() 計算出負數,對於 flex-basis 來說負數是不被允許的數值,會自動變成 auto
  • 當容器尺寸為 600px,是小於 48rem 的狀況,運算結果為:1679832px
  • 此時對於 flex-basis 來說是正確數值,但是由於 flex-shrink: 1; 的關係,他會縮小到符合容器尺寸。

這樣可以理解 flex-basis 到底發生了什麼事情了吧。另外,你要把 9999 換成 999 應該也是可以,只要你確認你的裝置不會這麼巨大的話應該是沒有問題的。

你說,誰還會需要 @media 呢?


所以說那個手機版

話說我今天去換輪胎,然後四傳一次要換四顆。換完師傅試車後跟我說,我的輪軸有異音應該要換了。

我難過。

所以如果你使用了 Media Query,在搭配上面兩種魔術方法,基本上可以更彈性的來調整內容。不過,這邊有一個比較大的前提是,你的內容不會因為彈性大小而造成差異,也就是我一開始說的,這些方式比較適合在文字展示區塊。

Hina Chen
偏執與強迫症的患者,算不上是無可救藥,只是我已經遇上我的良醫了。
Taipei