在 CSS 中,存在許多數學函數,這些函數能夠通過簡單的計算操作來生成某些屬性值,例如在現代 CSS 解決方案:CSS 數學函數一文中,我們詳細介紹了
在 現代 CSS 解決方案:CSS 原生支援的三角函數 一文中,給大家介紹了從 Chrome 111 開始也逐漸開始原生支援的三角函數:
而本文,我們將介紹另外一個非常有意思的數學函數 - round()。
簡單來說,round() CSS 函數的作用就是根據選定的舍入策略返回舍入數。
舉個例子,在 JavaScript 中,我們可以使用 Math.round()
返回一個數位四捨五入後最接近的整數。
譬如:
x = Math.round(20.49); //20
x = Math.round(20.5); //21
x = Math.round(-20.5); //-20
x = Math.round(-20.51); //-21
現在,CSS 藉助 round() 函數也有了相同的能力:
line-height: round(2.2, 1); /* 2 */
line-height: round(14.82, 1); /* 15 */
line-height: round(5.5, 1); /* 6 */
也就是說,round(2.2, 1)
中的 2.2 四捨五入後,最後的計算值是 2。
round() 的完整語法規則還是比較複雜的。完整的介紹可以看 MDN - round()。
使用它,可以完美實現類似於 JavaScript 中的如下幾個方法:
它的完整語法規則:
<round()> = round( <rounding-strategy>?, <valueToRound> , <roundingInterval> )
可以看到,它最多可以接收 3 個引數,並且第一個引數是可選引數:
<rounding-strategy>
:可選引數,表示舍入策略。 這可能是以下值之一:
up
: 相當於 JavaScript Math.ceil() 方法,將 valueToRound 向上舍入到 roundingInterval 最接近的整數倍。 這相當於 JavaScript Math.ceil() 方法。down
:將 valueToRound 向下舍入為 roundingInterval 最接近的整數倍。 這相當於 JavaScript Math.floor() 方法。nearest
:將 valueToRound 舍入為 roundingInterval 的最接近的整數倍,該倍數可以高於或低於該值。 如果 valueToRound 是上方和下方舍入目標之間的一半,則會向上舍入。 相當於 JavaScript Math.round()。to-zero
:將 valueToRound 舍入為 roundingInterval 接近/接近零的最接近整數倍。 這相當於 JavaScript Math.trunc() 方法。<valueToRound>
:需要被四捨五入的值。 必須是 <number>
、<dimension>
或 <percentage>
,或者解析為這些值之一的數學表示式。<roundingInterval>
:舍入的間隔規則。 這是一個 <number>
、<dimension>
或 <percentage>
,或者解析為這些值之一的數學表示式。基於此,舉幾個例子:
<div class="box-1"></div>
<div class="box-2"></div>
<div class="box-3"></div>
<div class="box-4"></div>
<div class="box-5"></div>
:root {
--rounding-interval: 25px;
}
div {
width: 100px;
background: rgba(255, 100, 0, .8);
}
div.box-1 {
height: round(nearest, 110px, var(--rounding-interval)); /* 最終計算值:100px */
}
div.box-2 {
height: round(up, 110px, var(--rounding-interval)); /* 最終計算值:125px */
}
div.box-3 {
height: round(down, 120px, var(--rounding-interval)); /* 最終計算值:100px */
}
div.box-4 {
height: round(to-zero, 120px, var(--rounding-interval)); /* 最終計算值:100px */
}
div.box-5 {
height: round(120px, var(--rounding-interval)); /* 最終計算值:125px */
}
結果如下:
圖中背景一個格子的大小是
25px
完整的 DEMO 可以看這裡 CodePen Demo - CSS Math Function Round() Demo
OK,鋪墊了那麼久,我們下面進入實戰環節。
那麼,round()
函數在 CSS 中有什麼具體的作用嗎?能應用到什麼地方?
在之前的 疑難雜症:運用 transform 導致文字模糊的現象探究 這篇文章中,我們介紹了一種基於transform 的模糊問題。
我們來回顧一下問題現象:
在我們的頁面中,經常會出現這樣的問題,一塊區域內的文字或者邊框,在展示的時候,變得特別的模糊,如下(資料經過脫敏處理):
正常而言,應該是這樣的:
emmm,可能大圖不是很明顯,我們取一細節對比,就非常直觀了:
那麼?什麼時候會觸發這種問題呢?在 Google 上,其實我們能搜到非常多類似的案例,總結而言:
transform: translate()
或者 transform: scale()
等 transform
操作時,容易出現這種問題當然,這只是必要條件,不是充分條件。繼續深入探究,會發現,必須還得同時滿足一些其它條件:
transform: translate()
或者 transform: scale()
後的計算值產生了非整數譬如,上述案例觸發的 CSS 程式碼如下:
.container {
position: absolute;
width: 1104px;
height: 475px;
top: 50%;
transform: translateY(-50%);
// ...
}
由於元素的高度為 475px
,translateY(-50%)
等於 237.5px
,非整數,才導致了內部的字型模糊。
但是,需要注意的是,並非所有產生的非整數都會導致了內部的字型模糊。
這裡有個簡單的示意:
還是上述的例子,當高度從 477px
一直調整到 469px
的過程中,只有 477px
和 475px
導致了模糊,而 473, 471, 469
則沒有。所以,這也只是引發模糊的一個必要條件。
在我實測的過程中還發現,這個現象基本只會發生在 dpr 為 1 的普通螢幕下。
類似於 MAC 的高清螢幕則不太會觸發這個問題。
dpr = 物理畫素 / 裝置獨立畫素,表示裝置畫素比。這個與我們通常說的視網膜屏(多倍屏,Retina屏)有關。裝置畫素比描述的是未縮放狀態下,物理畫素和裝置獨立畫素的初始比例關係。
那麼,為何會發生這種現象?針對這個問題,沒有找到特別官方的回答,普遍的認為是因為:
由於瀏覽器將圖層拆分到 GPU 以進行 3D 轉換,而非整數的畫素偏移,使得 Chrome 在字型渲染的時候,不是那麼的精確。
關於這個問題,感興趣的可以再看看這兩個討論:
在之前,上面的這個基於 transform 的問題基本是無解的,想要不模糊,就需要替換掉 transfrom
方法。
而在有了 round()
後,我們可以通過 round()
函數,保證作用了 transform: translate()
或者 transform: scale()
後的計算值一定是正整數,從而避免模糊問題。
譬如,原本的 CSS 如下:
.container {
width: 50vw;
height: 50vh;
transform: translate(-50%, -50%);
}
此時,transform: translate()
的實際最終計算值是會出現小數的。因此,我們可以使用 round()
函數進行取整:
.container {
width: 50vw;
height: 50vh;
transform: translate(round(-50%, 1px), round(-50%, 1px));
}
我們可以使用如下 JavaScript 程式碼,列印出 transform 實時的計算值。
window.addEventListener("resize", () => {
const transform = getComputedStyle(document.querySelectorAll("div")[0]).transform;
console.log("transform:", transform);
});
如果使用 transform: translate(-50%, -50%)
resize 整個頁面,可以看到如下列印值:
可以看到,此時,transform: matrix(1, 0, 0, 1, -50.5, -106.75)
的中的後兩位,其實就是 transform: translate(-50.5px, 106.75px)
,是存在小數值的。
而使用了 transform: translate(round(-50%, 1px), round(-50%, 1px))
後,將不會再出現小數值:
完整的程式碼,你可以戳這裡試一試:CodePen Demo -- round() Demo
藉由 round()
函數,我們成功的解決了一直以來,Chrome 中非常棘手的一個模糊問題!
round()
還有一個有趣用法。我們可以使用 round()
實現類似於 CSS Animation 中的 steps()
步驟動畫的效果。
我們來看這麼一個 DEMO:
<div></div>
@property --angle {
syntax: '<angle>';
inherits: false;
initial-value: 0deg;
}
div {
width: 200px;
height: 200px;
border-radius: 50%;
background: conic-gradient(#fc0, #fc0 15deg, transparent 15deg, transparent 30deg);
transform: rotate(var(--angle));
animation: propertyRotate 2s infinite linear;
}
@keyframes propertyRotate {
100% {
--angle: 360deg;
}
}
這裡,我們實現了這麼一個動畫效果:
我們可以利用 round()
,把一個連貫動畫,拆解成步驟動畫:
div {
// ...
// transform: rotate(var(--angle));
transform: rotate(round(var(--angle), 30deg));
}
上面,我們使用 transform: rotate(round(var(--angle), 30deg))
替換了 transform: rotate(var(--angle))
。
而 round(var(--angle), 30deg)
保證了其取值只能是 30deg 的倍數或者 0deg。因此,我們可以得到和使用 stpes()
步驟動畫一樣的效果:
上面使用了 round()
的動畫,和如下的動畫效果是一致的:
div {
transform: rotate(round(var(--angle), 30deg));
}
// 等同於
div {
transform: rotate(var(--angle));
animation: propertyRotate 2s infinite steps(12);
}
因此,使用 round()
,我們也可以輕鬆的實現類似如下的 Loading 動畫效果:
完整的程式碼,你可以戳這裡進行了解:CodePen Demo -- CSS Math Function Round() Animation Demo
好了,本文到此結束,希望本文對你有所幫助