掃二維碼與項目經(jīng)理溝通
我們在微信上24小時期待你的聲音
解答本文疑問/技術(shù)咨詢/運營咨詢/技術(shù)建議/互聯(lián)網(wǎng)交流
在 CSS 中,其實存在各種各樣的函數(shù)。具體分為:

目前成都創(chuàng)新互聯(lián)公司已為數(shù)千家的企業(yè)提供了網(wǎng)站建設(shè)、域名、網(wǎng)絡(luò)空間、網(wǎng)站改版維護、企業(yè)網(wǎng)站設(shè)計、葉集網(wǎng)站維護等服務(wù),公司將堅持客戶導(dǎo)向、應(yīng)用為本的策略,正道將秉承"和諧、參與、激情"的文化,與客戶和合作伙伴齊心協(xié)力一起成長,共同發(fā)展。
本文,將具體介紹其中的 CSS 數(shù)學(xué)函數(shù)(Math functions)中,已經(jīng)被瀏覽器大規(guī)模支持的 4 個:
為什么說是被瀏覽器大規(guī)模支持的?因為除了這 4 個目前已經(jīng)得到大規(guī)模支持的數(shù)學(xué)函數(shù)外,其實規(guī)范 CSS Values and Units Module Level 4[11] 已經(jīng)定義了諸如三角函數(shù)相關(guān) sin()、cos()、tan() 等,指數(shù)函數(shù)相關(guān) pow()、sqrt() 等等數(shù)學(xué)函數(shù),只是目前都處于實驗室階段,還沒有瀏覽器支持它們,需要給時間一點時間。
calc() 此 CSS[12] 函數(shù)允許在聲明 CSS 屬性值時執(zhí)行一些計算。
語法類似于:
{
width: calc(100% - 80px);
}一些需要注意的點:
看一個最常見的例子,頁面結(jié)構(gòu)如下:
Content
頁面的 g-footer 高為 80px,我們希望不管頁面多長,g-content 部分都可以占滿剩余空間,像是這樣:
這種布局使用 flex 的彈性布局可以輕松實現(xiàn),當(dāng)然,也可以使用 calc() 實現(xiàn):
.g-container {
height: 100vh;
}
.g-content {
height: calc(100vh - 80px);
}
.g-footer {
height: 80px;
}下面羅列一些 Calc() 的進階技巧。
注意,calc() 中的加減法與乘除法的差異:
{
font-size: calc(1rem + 10px);
width: calc(100px + 10%);
}可以看到,加減法兩邊的操作數(shù)都是需要單位的,而乘除法,需要一個無單位數(shù),僅僅表示一個倍率:
{
width: calc(100% / 7);
animation-delay: calc(1s * 3);
}
calc() 函數(shù)是可以嵌套使用的,像是這樣:
{
width: calc(100vw - calc(100% - 64px));
}此時,內(nèi)部的 calc() 函數(shù)可以退化寫成一個括號即可 (),所以上述代碼等價于:
{
width: calc(100vw - (100% - 64px));
}也就是嵌套內(nèi)的 calc(),calc 幾個函數(shù)字符可以省略。
calc() 支持不同單位的混合運算,對于長度,只要是屬于長度相關(guān)的單位都可以進行混合運算,包含這些:
這里有一個有意思的點,運算肯定是消耗性能的,早年間,有這樣一段 CSS 代碼,可以直接讓 Chrome 瀏覽器崩潰 Crash:
CSS 樣式如下:
div {
--initial-level-0: calc(1vh + 1% + 1px + 1em + 1vw + 1cm);
--level-1: calc(var(--initial-level-0) + var(--initial-level-0));
--level-2: calc(var(--level-1) + var(--level-1));
--level-3: calc(var(--level-2) + var(--level-2));
--level-4: calc(var(--level-3) + var(--level-3));
--level-5: calc(var(--level-4) + var(--level-4));
--level-6: calc(var(--level-5) + var(--level-5));
--level-7: calc(var(--level-6) + var(--level-6));
--level-8: calc(var(--level-7) + var(--level-7));
--level-9: calc(var(--level-8) + var(--level-8));
--level-10: calc(var(--level-9) + var(--level-9));
--level-11: calc(var(--level-10) + var(--level-10));
--level-12: calc(var(--level-11) + var(--level-11));
--level-13: calc(var(--level-12) + var(--level-12));
--level-14: calc(var(--level-13) + var(--level-13));
--level-15: calc(var(--level-14) + var(--level-14));
--level-16: calc(var(--level-15) + var(--level-15));
--level-17: calc(var(--level-16) + var(--level-16));
--level-18: calc(var(--level-17) + var(--level-17));
--level-19: calc(var(--level-18) + var(--level-18));
--level-20: calc(var(--level-19) + var(--level-19));
--level-21: calc(var(--level-20) + var(--level-20));
--level-22: calc(var(--level-21) + var(--level-21));
--level-23: calc(var(--level-22) + var(--level-22));
--level-24: calc(var(--level-23) + var(--level-23));
--level-25: calc(var(--level-24) + var(--level-24));
--level-26: calc(var(--level-25) + var(--level-25));
--level-27: calc(var(--level-26) + var(--level-26));
--level-28: calc(var(--level-27) + var(--level-27));
--level-29: calc(var(--level-28) + var(--level-28));
--level-30: calc(var(--level-29) + var(--level-29));
--level-final: calc(var(--level-30) + 1px);
border-width: var(--level-final);
border-style: solid;
}可以看到,從 --level-1 到 --level-30,每次的運算量都是成倍的增長,最終到 --level-final 變量,展開將有 2^30 = 1073741824 個 --initial-level-0 表達式的內(nèi)容。
并且,每個 --initial-level-0 表達式的內(nèi)容 -- calc(1vh + 1% + 1px + 1em + 1vw + 1cm),在瀏覽器解析的時候,也已經(jīng)足夠復(fù)雜。
混合在一起,就導(dǎo)致了瀏覽器的 BOOM(Chrome 70之前的版本),為了能看到效果,我們將上述樣式賦給某個元素被 hover 的時候,得到如下效果:
css-crash
當(dāng)然,這個 BUG 目前已經(jīng)被修復(fù)了,我們也可以通過這個小 DEMO 了解到,一是 calc 是可以進行不同單位的混合運算的,另外一個就是注意具體使用的時候如果計算量巨大,可能會導(dǎo)致性能上較大的消耗。
當(dāng)然,不要將長度單位和非長度單位混合使用,像是這樣:
{
animation-delay: calc(1s + 1px);
}
calc() 函數(shù)非常重要的一個特性就是能夠搭配 CSS 自定義以及 CSS @property 變量一起使用。
最簡單的一個 DEMO:
:root {
--width: 10px;
}
div {
width: calc(var(--width));
}當(dāng)然,這樣看上去,根本看不出這樣的寫法的作用,好像沒有什么意義。實際應(yīng)用場景中,會比上述的 DEMO 要稍微復(fù)雜一些。
假設(shè)我們要實現(xiàn)這樣一個 loading 動畫效果,一開始只有 3 個球:
可能的寫法是這樣,我們給 3 個球都添加同一個旋轉(zhuǎn)動畫,然后分別控制他們的 animation-delay:
.item:nth-child(1) {
animation: rotate 3s infinite linear;
}
.item:nth-child(2) {
animation: rotate 3s infinite -1s linear;
}
.item:nth-child(3) {
animation: rotate 3s infinite -2s linear;
}如果有一天,這個動畫需要擴展成 5 個球的話,像是這樣:
我們就不得已,得去既添加 HTML,又修改 CSS。而如果借助 Calc 和 CSS 變量,這個場景就可以稍微簡化一下。
假設(shè)只有 3 個球:
我們通過 HTML 的 Style 標(biāo)簽,傳入 --delay 變量,在 CSS 中直接使用它們:
.g-item {
animation: rotate 3s infinite linear;
animation-delay: calc(var(--delay) * -1s);
}
@keyframes rotate {
to {
transform: rotate(360deg);
}
}而當(dāng)動畫修改成 5 個球時,我們就不需要修改 CSS,直接修改 HTML 即可,像是這樣:
核心的 CSS 還是這一句,不需要做任何修改:
{
animation-delay: calc(var(--delay) * -1s);
}完整的 DEMO,你可以戳這里:CodePen Demo -- Calc & CSS Variable Demo[13]
還是上述的 Loading 動畫效果,如果我的 HTML 標(biāo)簽中,有一個標(biāo)簽忘記填充 --delay 的值了,那會發(fā)生什么?
像是這樣:
{
animation-delay: calc(var(--delay) * -1s);
}由于 HTML 標(biāo)簽沒有傳入 --delay 的值,并且在 CSS 中向上查找也沒找到對應(yīng)的值,此時,animation-delay: calc(var(--delay) * -1s) 這一句其實是無效的,相當(dāng)于 animation-delay: 0,效果也就是少了個球的效果:
所以,基于這種情況,可以利用 CSS 自定義變量 var() 的 fallback 機制:
{
// (--delay, 1) 中的 1 是個容錯機制
animation-delay: calc(var(--delay, 1) * -1s);
}此時,如果沒有讀取到任何 --delay 值,就會使用默認(rèn)的 1 與 -1s 進行運算。
很多人在使用 CSS 的時候,會嘗試字符串的拼接,像是這樣:
:root {
--urlA: 'url(https://s1.ax1x.com/2022/03/07/';
--urlB: ')';
}
div {
width: 400px;
height: 400px;
background-image: calc(var(--urlA) + var(--url) + var(--urlB));
}這里想利用 calc(var(--urlA) + var(--url) + var(--urlB)) 拼出完整的在 background-image 中可使用的 URL url(https://s1.ax1x.com/2022/03/07/bsBD1I.png)。
然而,這是不被允許的(無法實現(xiàn)的)。calc 的沒有字符串拼接的能力。
唯一可能完成字符串拼接的是在元素的偽元素的 content 屬性中。但是也不是利用 calc。
來看這樣一個例子,這是錯誤的:
:root {
--stringA: '123';
--stringB: '456';
--stringC: '789';
}
div::before {
content: calc(var(--stringA) + var(--stringB) + var(--stringC));
}此時,不需要 calc,直接使用自定義變量相加即可。
因此,正確的寫法:
:root {
--stringA: '123';
--stringB: '456';
--stringC: '789';
}
div::before {
content: var(--stringA) + var(--stringB) + var(--stringC);
}此時,內(nèi)容可以正常展示:
再強調(diào)一下,calc 的沒有字符串拼接的能力,如下的使用方式都是無法被識別的錯誤語法:
.el::before {
// 不支持字符串拼接
content: calc("My " + "counter");
}
.el::before {
// 更不支持字符串乘法
content: calc("String Repeat 3 times" * 3);
}
本文是現(xiàn)代 CSS 解決方案系列文章的第二篇,首發(fā)公眾號,希望通過一些更易理解的語言、更直觀的 DEMO,講述在如今如何更好的使用 CSS 去提升我們網(wǎng)站的體驗,去提高我們的技巧。
好了,本文到此結(jié)束,希望對你有所幫助:)
[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: ht
分享標(biāo)題:現(xiàn)代CSS解決方案:CSS數(shù)學(xué)函數(shù)之Calc
地址分享:http://uogjgqi.cn/article/cdooghg.html

我們在微信上24小時期待你的聲音
解答本文疑問/技術(shù)咨詢/運營咨詢/技術(shù)建議/互聯(lián)網(wǎng)交流