CSS calc()的完整指南
CSS 具有calc()
执行基本数学运算的特殊功能。
.main-content { /* 从 100vh 减去 80px */ height: calc(100vh - 80px); }
在本指南中,我将介绍有关此非常有用的功能的所有知识。 推荐阅读之前写过一篇类似文章《css3 calc()属性介绍以及自适应布局使用方法》。
calc()用于值
可以使用calc()
函数的唯一地方是值。请看下面这些示例,其中我们为许多不同的属性设置值。
.el { font-size: calc(3vw + 2px); width: calc(100% - 20px); height: calc(100vh - 20px); padding: calc(1vw + 5px); }
它也可以仅用于属性的一部分,例如:
.el { margin: 10px calc(2vw + 5px); border-radius: 15px calc(15px / 3) 4px 2px; transition: transform calc(1s - 120ms); }
它甚至可以成为构成属性一部分的另一个函数的一部分!例如,在颜色渐变属性中使用 calc():
.el { background: #1E88E5 linear-gradient( to bottom, #1E88E5, #1E88E5 calc(50% - 10px), #3949AB calc(50% + 10px), #3949AB ); }
calc()用于长度和其他数字事物
请注意,以上所有示例本质上都是基于数字的。我们将讨论一些数字使用方法的注意事项(因为有时不需要单位),但这是用于数字数学的,而不是字符串或类似的东西。
.el { /* 不可以! */ counter-reset: calc("My " + "counter"); } .el::before { /* 不可以! */ content: calc("Candyman " * 3); }
CSS 有很多长度,它们都可以用于 calc():
- px
- %
- em
- rem
- in
- mm
- cm
- pt
- pc
- ex
- ch
- vh
- vw
- vmin
- vmax
当然没有单位数也可以接受,例如line-height: calc(1.2 * 1.2);
,如transform: rotate(calc(10deg * 5));
。
当我们不用 calc()去执行任何计算时,它仍然有效:
.el { /* 有点怪,不过还好 */ width: calc(20px); }
不接受媒体查询
如果 calc()使用正确(将长度单位用作属性的值),则将其 calc()应用于媒体查询时无法使用。
@media (max-width: 40rem) {} /* 不可以 */ @media (min-width: calc(40rem + 1px)) {}
混合单元
这也许是最能体现 calc()价值的功能!上面几乎每个示例都已经做到了这一点,但只是要指出一点,这里混合了不同的长度单元:
width: calc(100% - 20px);
这就是说:元素的宽度为负 20 像素。
有字面上没有办法预先计算出在流体宽度的情况下单独的像素值。换句话说,calc()无法使用 Sass 之类的东西进行预处理,以尝试完成 polyfill。不需要,因为浏览器支持很好。但是要点是,当我们以这种方式混合单位时,必须在浏览器中(“运行时”)完成操作,这是的大部分值 calc()。
这是混合单元的其他一些示例:
transform: rotate(calc(1turn + 45deg));
animation-delay: calc(1s + 15ms);
这些可能会被预处理,因为它们混合了与运行时确定的任何单位都不相关的单位。
与预处理器数学比较
我们刚刚介绍了无法预处理 calc()可以做的最有用的事情。但是有一点需要注意,例如,Sass 内置了数学功能,因此可以执行以下操作:
$padding: 1rem; .el[data-padding="extra"] { padding: $padding + 2rem; margin-bottom: $padding * 2; }
甚至带有单位的数学也可以在这里工作,将相同单位的值相加或乘以无单位数。但是你不能混合使用单位,并且它也有类似的限制 calc()(例如,乘法和除法必须使用无单位的数字)。
数学计算
例如,通过 calc()假设你需要计算准确 1 / 7 个元素的宽度
.el { /* 这更容易理解 */ width: calc(100% / 7); /* 传统的写法 */ width: 14.2857142857%; }
这可能会在某种自行创建的 CSS API 中获得成功,例如:
[data-columns="7"] .col { width: calc(100% / 7); } [data-columns="6"] .col { width: calc(100% / 6); } [data-columns="5"] .col { width: calc(100% / 5); } [data-columns="4"] .col { width: calc(100% / 4); } [data-columns="3"] .col { width: calc(100% / 3); } [data-columns="2"] .col { width: calc(100% / 2); }
calc()的数学运算符
数学运算符+,-,*,和/ 对于大家来说并不陌生。但是这里在使用它们的方式上有所不同。
加号(+)和减号(-)要求两个数字均为长度
.el { /* 有效 👍 */ margin: calc(10px + 10px); /* 无效 👎 */ margin: calc(10px + 5); }
无效的值会使整个单独的声明无效。
除(/)要求第二个数字不能为无单位
.el { /* 有效👍 */ margin: calc(30px / 3); /* 无效 👎 */ margin: calc(30px / 10px); /* 无效👎 (不能除以 0) */ margin: calc(30px / 0); }
乘法(*)要求数字之一为无单位
.el { /* 有效👍 */ margin: calc(10px * 3); /* 有效 👍 */ margin: calc(3 * 10px); /* 无效 👎 */ margin: calc(30px * 3px); }
空格很重要
可以进行加法和减法。
.el { /* 有效👍 */ font-size: calc(3vw + 2px); /* 无效 👎 */ font-size: calc(3vw+2px); /* 有效 👍 */ font-size: calc(3vw - 2px); /* 无效 👎 */ font-size: calc(3vw-2px); }
负数是可以的(例如calc(5vw - - 5px)
),但这是一个例子,说明空白不仅是必需的,而且是有用的。
之所以需要在+和-之间设置间距,实际上是因为解析问题。例如,2px-3px
被解析为数字“2”和单位“px-3px”,+还有其他问题,比如“被数字语法占用了”。我猜空白应该与自定义属性的- -
语法有关,但不是!
乘法和除法不需要运算符周围的空格。但是我个人建议为了可读性还是有必要加上
外部的空白无关紧要。我们甚至可以根据需要进行换行:
.el { /* Valid 👍 */ width: calc( 100% / 3 ); }
但是请注意这一点:calc 和()括号之间没有空格。
.el { /* 无效👎 */ width: calc (100% / 3); }
嵌套 calc(calc())
.el { width: calc( calc(100% / 3) - calc(1rem * 2) ); }
calc()里面嵌套的 calc()是可以忽略掉的,因为它的父级是单独工作的:
.el { width: calc( (100% / 3) - (1rem * 2) ); }
在这种情况下,即使没有括号,也会遵循数学中的加减乘除的“操作顺序”规则。也就是说,除法和乘法首先发生(在加法和减法之前),因此根本不需要括号。可以这样写:
.el { width: calc(100% / 3 - 1rem * 2); }
如果我们确实需要先进行加法或减法,则需要给它们加上必要的括号就可以啦。
.el { width: calc(100% + 2rem / 2); width: calc((100% + 2rem) / 2); }
CSS 自定义属性和 calc()使用
除了 calc()混合单元的惊人功能外,接下来最令人惊讶的是 calc()可将其与自定义属性一起使用。自定义属性可以在随后的计算中使用:
html { --spacing: 10px; } .module { padding: calc(var(--spacing) * 2); }
我敢肯定,你可以想象一个 CSS 设置,其中设置一堆 CSS 自定义属性,然后让其余的 CSS 根据需要使用它们,从而在顶部进行大量配置。
自定义属性也可以相互引用。下面是一个例子,其中使用了一些数学(注意,首先缺少 calc()函数),然后再应用。(它最终必须在 calc()中。)
html { --spacing: 10px; --spacing-L: var(--spacing) * 2; --spacing-XL: var(--spacing) * 3; } .module[data-spacing="XL"] { padding: calc(var(--spacing-XL)); }
你可能不喜欢这样,因为你需要记住 calc()在哪里使用该属性,但从可读性的角度来看,这是可能的,而且可能很有趣。
自定义属性可以来自 HTML,这有时是一件非常酷和有用的事情。
<div style="--index: 1;"> ... </div> <div style="--index: 2;"> ... </div> <div style="--index: 3;"> ... </div> div { /* 索引值来自 HTML(带有回退) */ animation-delay: calc(var(--index, 1) * 0.2s); }
添加单位
如果你处于这样一种情况:没有单位的数字更容易存储,或者提前用没有单位的数字进行数学运算,你可以一直等到你应用数字乘以 1 和单位来添加单位。
html { --importantNumber: 2; } .el { /* Number stays 2, but it has a unit now */ padding: calc(var(--importantNumber) * 1rem); }
色彩值的使用
像 RGB 和 HSL 这样的颜色格式有可以使用 calc()处理的数字。例如,设置一些基本 HSL 值,然后更改它们以形成自己创建的系统(示例):
html { --H: 100; --S: 100%; --L: 50%; } .el { background: hsl( calc(var(--H) + 20), calc(var(--S) - 10%), calc(var(--L) + 30%) ) }
不能将 calc()和 attr()合并
CSS 中的attr()
函数看起来很吸引人,就像你可以从 HTML 中提取属性值并使用它们一样。但是
<div data-color="red">...</div> div { /* 不行 */ color: attr(data-color); }
这里没有“类型”,所以 attr()的唯一用途是与 content 属性一起使用的字符串。这意味着这样是有效的:
div::before { content: attr(data-color); }
我之所以提到这一点,是因为试图以这种方式提取一个数字以用于计算可能会很有诱惑力,例如:
<div class="grid" data-columns="7" data-gap="2">...</div> .grid { display: grid; /* 这两项工作都没有 */ grid-template-columns: repeat(attr(data-columns), 1fr); grid-gap: calc(1rem * attr(data-gap)); }
幸运的是,这并不重要,因为 HTML 中的自定义属性同样有用甚至更多!
<div class="grid" style="--columns: 7; --gap: 2rem;">...</div> .grid { display: grid; /* 好了! */ grid-template-columns: repeat(var(--columns), 1fr); grid-gap: calc(var(--gap)); }
浏览器工具
浏览器开发工具会倾向于在样式表中编写 calc()时向你显示它。
如果需要计算出的值,可以使用“计算”选项卡(在所有浏览器的 DevTools 中,至少是我所知道的)向你显示。
浏览器支持
该浏览器支持数据来自Caniuse,它具有更多详细信息。数字表示浏览器支持该版本及更高版本的功能。
如果确实需要支持更早版本(例如 IE 8 或 Firefox 3.6),通常的技巧是在使用 calc()以下属性或值之前添加另一个属性或值:
.el { width: 92%; /* Fallback */ width: calc(100% - 2rem); }
当然也有很多已知的问题 calc(),不过它们都是针对旧浏览器的。
- Firefox <59 不支持 calc() 颜色功能。范例: color: hsl(calc(60 * 2), 100%, 50%)。
- 当 IE 9 – 11 用于任何值 box-shadow 时,将不会呈现该 属性 calc()。
- width: calc() 用在表格单元格上时,在 IE 9-11 和 Edge 都不支持
如果要将 calc()用作包含视口单位等的流体类型情况下,请确保包含使用 rem 或 em 的单位,以便用户仍然可以根据需要通过放大或缩小来控制字体的上下起伏。
码云笔记 » CSS calc()的完整指南