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 {
/* 索引值来自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 {
/* 不行 */
color: attr(data-color);
}
这里没有“类型”,所以attr()的唯一用途是与content属性一起使用的字符串。这意味着这样是有效的:
div::before {
content: attr(data-color);
}
我之所以提到这一点,是因为试图以这种方式提取一个数字以用于计算可能会很有诱惑力,例如:
.grid {
display: grid;
/* 这两项工作都没有 */
grid-template-columns: repeat(attr(data-columns), 1fr);
grid-gap: calc(1rem * attr(data-gap));
}
幸运的是,这并不重要,因为HTML中的自定义属性同样有用甚至更多!
.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的单位,以便用户仍然可以根据需要通过放大或缩小来控制字体的上下起伏。