現代 CSS 解決方案:數學函數之 Min、Max、Clamp
在 CSS 中,其實存在各種各樣的函數。具體分為:
- Transform functions[1]
- Math functions[2]
- Filter functions[3]
- Color functions[4]
- Image functions[5]
- Counter functions[6]
- Font functions[7]
- Shape functions[8]
- Reference functions[9]
- CSS grid functions[10]
本文,將具體介紹其中的 CSS 數學函數(Math functions)中,已經被瀏覽器大規模支持的 4 個:
- calc()
- min()
- max()
- clamp()
為什么說是被瀏覽器大規模支持的?因為除了這 4 個目前已經得到大規模支持的數學函數外,其實規范 CSS Values and Units Module Level 4[11] 已經定義了諸如三角函數相關 sin()、cos()、tan() 等,指數函數相關 pow()、sqrt() 等等數學函數,只是目前都處于實驗室階段,還沒有瀏覽器支持它們,需要給時間一點時間。
上一篇文章我們詳細講述了 calc(),本文我們將探討一下另外 3 個。關于 calc(),你可以戳這里:
現代 CSS 解決方案:CSS 數學函數之 calc。
min()、max()、clamp()
min()、max()、clamp() 適合放在一起講。它們的作用彼此之間有所關聯。
- max():從一個逗號分隔的表達式列表中選擇最大(正方向)的值作為屬性的值。
- min():從一個逗號分隔的表達式列表中選擇最小的值作為屬性的值。
- clamp():把一個值限制在一個上限和下限之間,當這個值超過最小值和最大值的范圍時,在最小值和最大值之間選擇一個值使用。
由于在現實中,有非常多元素的的屬性不是一成不變的,而是會根據上下文、環境的變化而變化。
譬如這樣一個布局:
<div class="container"></div>
.container {
height: 100px;
background: #000;
}
效果如下,.container 塊它會隨著屏幕的增大而增大,始終占據整個屏幕:
對于一個響應式的項目,我們肯定不希望它的寬度會一直變大,而是當達到一定的閾值時,寬度從相對單位變成了絕對單位,這種情況就適用于 min(),簡單改造下代碼:
.container {
width: min(100%, 500px);
height: 100px;
background: #000;
}
容器的寬度值會在 width: 100% 與 width: 500px 之間做選擇,選取相對小的那個。
在屏幕寬度不足 500px 時候,也就表現為 width: 100%,反之,則表現為 width: 500px:
同理,在類似的場景,我們也可以使用 max() 從多個值中,選取相對更大的值。
min()、max() 支持多個值的列表
min()、max() 支持多個值的列表,譬如 width: max(1px, 2px, 3px, 50px)。
當然,對于上述表達:
width: max(1px, 2px, 3px, 50px) 其實等于 width: 50px。因此,對于 min()、max() 的具體使用而言,最多應該只包含一個具體的絕對單位。否則,這樣的像上述這種代碼,雖然語法支持,但是任何情況下,計算值都是確定的,其實沒有意義。
配合 calc
min()、max()、clamp() 都可以配合 calc 一起使用。
譬如:
div {
width: max(50vw, calc(300px + 10%));
}
在這種情況下,calc 和相應包裹的括號可以省略,因此,上述代碼又可以寫成:
div {
width: max(50vw, 300px + 10%);
}
基于 max、min 模擬 clamp
現在,有這樣一種場景,如果,我們又需要限制最大值,也需要限制最小值,怎么辦呢?
像是這樣一個場景,**字體的大小,最小是 12px,隨著屏幕的變大,逐漸變大,但是為了避免老人機現象(隨著屏幕變大,無限制變大),我們還需要限制一個最大值 20px。
我們可以利用 vw 來實現給字體賦動態值,假設在移動端,設備寬度的 CSS 像素為 320px 時,頁面的字體寬度最小為 12px,換算成 vw 即是 320 / 100 = 3.2,也就是 1vw 在 屏幕寬度為 320px 時候,表現為 3.2px,12px 約等于 3.75 vw。
同時,我們需要限制最大字體值為 20px,對應的 CSS 如下:
p {
font-size: max(12px, min(3.75vw, 20px));
}
看看效果:
通過 max()、min() 的配合使用,以及搭配一個相對單位 vw,我們成功地給字體設置了上下限,而在這個上下限之間實現了動態變化。
當然,上面核心的這一段 max(12px, min(3.75vw, 20px)) 看上去有點繞,因此,CSS 推出了 clamp() 簡化這個語法,下面兩個寫法是等價的:
p {
font-size: max(12px, min(3.75vw, 20px));
// 等價于
font-size: clamp(12px, 3.75vw, 20px);
}
clamp()
clamp() 函數的作用是把一個值限制在一個上限和下限之間,當這個值超過最小值和最大值的范圍時,在最小值和最大值之間選擇一個值使用。它接收三個參數:最小值、首選值、最大值。
有意思的是,clamp(MIN, VAL, MAX) 其實就是表示 max(MIN, min(VAL, MAX))。
使用 vw 配合 clamp 實現響應式布局
我們繼續上面的話題。
在不久的過去,移動端的適配方面,使用更多的 rem 適配方案,可能會借助一些現成的庫,類似于 flexible.js、hotcss.js 等庫。rem 方案比較大的一個問題在于需要一段 JavaScript 響應視口變化,重設根元素的 font-size,并且,使用 rem 多少有點 hack 的感覺。
在現在,在移動端適配,我們更為推崇的是 vw 純 CSS 方案,與 rem 方案類似,它的本質也是頁面的等比例縮放。它的一個問題在于,如果僅僅使用 vw,隨著屏幕的不斷變大或者縮小,內容元素將會一直變大變小下去,這也導致了在大屏幕下,許多元素看著實在太大了!
因此,我們需要一種能夠控制最大、最小閾值的方式,像是這樣:
此時,clamp 就能非常好的派上用場,還是我們上述的例子,這一段代碼 font-size: max(12px, min(3.75vw, 20px));,就能將字體限制在 12px - 20px 的范圍內。
因此,對于移動端頁面而言,所有涉及長度的單位,我們都可以使用 vw 進行設置。而諸如字體、內外邊距、寬度等不應該完全等比例縮放的,采用 clamp() 控制最大最小閾值。
在 Modern Fluid Typography Using CSS Clamp[12] 一文中,對使用 clamp() 進行流式響應式布局還有更為深入的探討,感興趣的可以深入閱讀。
總結而言,對于移動端頁面,我們可以以 vw 配合 clamp() 的方式,完成整個移動端布局的適配。它的優勢在于
- 沒有額外 JavaScript 代碼的引入,純 CSS 解決方案。
- 能夠很好地控制邊界閾值,合理的進行縮放展示。
反向響應式變化
還有一個技巧,利用 clamp() 配合負值,我們也可以反向操作,得到一種屏幕越大,字體越小的反向響應式效果:
p {
font-size: clamp(20px, -5vw + 96px, 60px);
}
看看效果:
這個技巧挺有意思的,由于 -5vw + 96px 的計算值會隨著屏幕的變小而增大,實現了一種反向的字體響應式變化。
總結
總結一下,合理運用 min()、max()、clamp(),是構建現代響應式布局的重點,我們可以告別傳統的需要 JavaScript 輔助的一些方案,基于 CSS 這些數學函數即可完成所有的訴求。
一些進階閱讀非常好的文章:
- A guide to the min(), max(), and clamp() CSS functions[13]。
- Modern Fluid Typography Using CSS Clamp[14]。
最后
好了,本文到此結束,希望本文對你有所幫助 :)
參考資料
- [1]Transform functions: https://developer.mozilla.org/en-US/docs/Web/CSS/CSS_Functions#transform_functions。
- [2]Math functions: https://developer.mozilla.org/en-US/docs/Web/CSS/CSS_Functions#math_functions。
- [3]Filter functions: https://developer.mozilla.org/en-US/docs/Web/CSS/CSS_Functions#filter_functions。
- [4]Color functions: https://developer.mozilla.org/en-US/docs/Web/CSS/CSS_Functions#color_functions。
- [5]Image functions: https://developer.mozilla.org/en-US/docs/Web/CSS/CSS_Functions#image_functions。
- [6]Counter functions: https://developer.mozilla.org/en-US/docs/Web/CSS/CSS_Functions#counter_functions。
- [7]Font functions: https://developer.mozilla.org/en-US/docs/Web/CSS/CSS_Functions#font_functions。
- [8]Shape functions: https://developer.mozilla.org/en-US/docs/Web/CSS/CSS_Functions#shape_functions。
- [9]Reference functions: https://developer.mozilla.org/en-US/docs/Web/CSS/CSS_Functions#reference_functions。
- [10]CSS grid functions: https://developer.mozilla.org/en-US/docs/Web/CSS/CSS_Functions#css_grid_functions。
- [11]CSS Values and Units Module Level 4: https://drafts.csswg.org/css-values/#math。
- [12]Modern Fluid Typography Using CSS Clamp: https://www.smashingmagazine.com/2022/01/modern-fluid-typography-css-clamp/。
- [13]A guide to the min(), max(), and clamp() CSS functions: https://blog.logrocket.com/min-max-clamp-css-functions/。
- [14]Modern Fluid Typography Using CSS Clamp: https://www.smashingmagazine.com/2022/01/modern-fluid-typography-css-clamp/。