CSS 计算函数 指南

Avatar of Chris Coyier
Chris Coyier

DigitalOcean 为您旅程的每个阶段提供云产品。 立即开始使用 $200 免费积分!

CSS 有一个特殊的 calc() 函数用于进行基本数学运算。 在本指南中,我们将介绍有关此非常有用的函数的几乎所有知识。

这是一个 示例

.main-content {
  /* Subtract 80px from 100vh */
  height: calc(100vh - 80px);
}

在本指南中,我们将介绍有关此非常有用的函数的几乎所有知识。

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 {
  /* Nope! */
  counter-reset: calc("My " + "counter");
}
.el::before {
  /* Nope! */
  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));

您也可以执行任何计算,它仍然有效

.el {
  /* Little weird but OK */
  width: calc(20px);
}

媒体查询不可用

calc() 正确使用时(长度单位用作属性的值),遗憾的是,calc() 应用于媒体查询时将不起作用。

@media (max-width: 40rem) {
  /* Narrower or exactly 40rem */
}

/* Nope! */
@media (min-width: calc(40rem + 1px)) {
  /* Wider than 40rem */
}

将来有一天会很酷,因为您可以以一种相当合乎逻辑的方式(如上面所示)执行 互斥媒体查询

混合单位 🎉

这可能是 calc() 最有价值的功能! 以上几乎所有示例都已完成此操作,但为了说明这一点,这里混合了不同的单位

/* Percentage units being mixed with pixel units */
width: calc(100% - 20px);

这意味着:元素的宽度减去 20 像素。

在流体宽度情况下,根本无法仅用像素预先计算该值。 换句话说,您不能用 Sass 等东西预处理 calc() 作为尝试完成填充。 当然您不需要这样做,因为 浏览器支持 很好。 但是重点是,当您以这种方式混合单位时,必须在浏览器中(在“运行时”)进行处理,这是 calc() 的大部分价值所在。

以下是一些混合单位的其他示例

transform: rotate(calc(1turn + 45deg));

animation-delay: calc(1s + 15ms);

这些可能可以预处理,因为它们混合了不依赖于运行时确定的任何内容的单位。

与预处理器数学的比较

我们刚刚介绍了您无法预处理 calc() 可以执行的最有用的操作。 但它们确实有一点重叠。 例如,Sass 中内置了数学运算,因此您可以执行以下操作:

$padding: 1rem;

.el[data-padding="extra"] {
  padding: $padding + 2rem; // processes to 3rem;
  margin-bottom: $padding * 2; // processes to 2rem; 
}

即使是带有单位的数学运算也能在那里起作用,将相同单位的值加在一起或乘以无单位数字。 但是您不能混合单位,并且它具有与 calc() 类似的限制(例如,乘法和除法必须使用无单位数字)。

显示数学运算

即使您没有使用仅 calc() 才能实现的功能,它也可以用于 “在 CSS 中显示您的工作原理”。 例如,假设您需要精确计算元素宽度的 17...

.el {
  /* This is easier to understand */
  width: calc(100% / 7);

  /* Than this is */
  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 {
  /* Valid 👍 */
  margin: calc(10px + 10px);

  /* Invalid 👎 */
  margin: calc(10px + 5);
}

无效值将使整个单个声明无效。

除法 (/) 要求第二个数字是无单位的

.el {
  /* Valid 👍 */
  margin: calc(30px / 3);

  /* Invalid 👎 */
  margin: calc(30px / 10px);

  /* Invalid 👎 (can't divide by 0) */
  margin: calc(30px / 0);
}

乘法 (*) 要求其中一个数字是无单位的

.el {
  /* Valid 👍 */
  margin: calc(10px * 3);

  /* Valid 👍 */
  margin: calc(3 * 10px);

  /* Invalid 👎 */
  margin: calc(30px * 3px);
}

空格很重要

好吧,这对加法减法很重要。

.el {
  /* Valid 👍 */
  font-size: calc(3vw + 2px);

  /* Invalid 👎 */
  font-size: calc(3vw+2px);

  /* Valid 👍 */
  font-size: calc(3vw - 2px);

  /* Invalid 👎 */
  font-size: calc(3vw-2px);
}

负数是可以的(例如 calc(5vw - -5px)),但这是一个空格不仅是必需的,而且也有帮助的示例。

Tab Atkins 告诉我,围绕 +- 的空格要求实际上是由于解析问题。 我不敢说我完全理解,但例如,2px-3px 解析为数字“2”和单位“px-3px”,这对任何人都没有好处,而 + 有其他问题,例如“被数字语法消耗”。 我本以为空格必须与自定义属性的 -- 语法有关,但事实并非如此!

乘法和除法不需要在运算符周围留空格。 但我认为,为了可读性和对其他运算符的肌肉记忆,最好包含空格。

外部周围的空格无关紧要。 您可以随意进行换行

.el {
  /* Valid 👍 */
  width: calc(
    100%     /   3
  );
}

但要小心:calc() 和左括号之间不要留空格。

.el {
  /* Invalid 👎 */
  width: calc (100% / 3);
}

嵌套 calc(calc());

您可以这样做,但永远没有必要。 这与使用额外的括号集(没有 calc() 部分)相同。 例如

.el {
  width: calc(
    calc(100% / 3)
    -
    calc(1rem * 2)
  );
}

您不需要在 calc() 中使用这些,因为括号本身就可以起作用

.el {
  width: calc(
   (100% / 3)
    -
   (1rem * 2)
  );
}

在这种情况下,“运算顺序”可以帮助我们,即使没有括号。 也就是说,除法和乘法执行(在加法和减法之前),因此根本不需要括号。 它可以这样写

.el {
  width: calc(100% / 3 - 1rem * 2);
}

但如果您觉得使用括号可以提高清晰度,请随意使用。如果运算顺序对您不利(例如,您确实需要先进行加法或减法),则需要使用括号。

.el {
  /* This */
  width: calc(100% + 2rem / 2);

  /* Is very different from this */
  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,这有时非常有用。(例如,请查看 Splitting.js 如何将索引添加到单词/字符)。

<div style="--index: 1;"> ... </div>
<div style="--index: 2;"> ... </div>
<div style="--index: 3;"> ... </div>
div {
  /* Index value comes from the HTML (with a fallback) */
  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 {
  /* Nope */
  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;

  /* Neither of these work */
  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;

  /* Yep! */
  grid-template-columns: repeat(var(--columns), 1fr);
  grid-gap: calc(var(--gap));
}

浏览器工具

浏览器 DevTools 会倾向于向您展示您在样式表中编写的 calc()

Firefox DevTools - 规则

如果您需要弄清楚计算后的值,则有一个“计算”选项卡(在所有浏览器 DevTools 中,至少我知道的那些),它会向您显示它。

Chrome DevTools - 计算

浏览器支持

此浏览器支持数据来自 Caniuse,其中包含更多详细信息。数字表示浏览器从该版本开始支持该功能。

桌面

ChromeFirefoxIEEdgeSafari
19*4*11126*

移动/平板电脑

Android ChromeAndroid FirefoxAndroidiOS Safari
1271271276.0-6.1*

如果您确实需要支持超级远古的浏览器(例如 IE 8 或 Firefox 3.6),通常的做法是在使用 calc() 的属性或值之前添加另一个属性或值。

.el {
  width: 92%; /* Fallback */
  width: calc(100% - 2rem);
}

calc() 也有一些已知问题,但它们都是针对旧浏览器的。 Can I use... 列出了 13 个。以下是一些

  • Firefox <59 不支持 calc() 在颜色函数上。示例:color: hsl(calc(60 * 2), 100%, 50%)
  • IE 9-11 在 calc() 用于任何值时不会呈现 box-shadow 属性。
  • IE 9-11 和 Edge 均不支持 width: calc() 在表格单元格上。

用例派对

我询问了一些 CSS 开发人员他们上次使用 calc() 的时间,以便我们可以在这里了解一下其他人如何在他们的日常工作中使用它。

我用它来创建一个全屏实用程序类:.full-bleed { width: 100vw; margin-left: calc(50% - 50vw); } 我会说 calc() 是我最喜欢的 CSS 东西前三名。

我用它来为固定页脚腾出空间。

我用它来设置一些 流体类型 / 动态排版... 基于最小值、最大值和视窗单位变化率计算的 font-size。不仅仅是 font-size,还有 line-height

如果您将 calc() 用作流体类型情况的一部分,该情况涉及视窗单位等,请确保您包含一个使用 remem 的单位,以便用户仍然可以控制根据需要放大或缩小字体。

我真的很喜欢的一个是使用“内容宽度”自定义属性,然后使用它来创建我需要的间距,例如边距:.margin { width: calc( (100vw - var(--content-width)) / 2); }

我用它来创建一个跨浏览器首字母下沉组件。这是一部分

.drop-cap { --drop-cap-lines: 3; font-size: calc(1em * var(--drop-cap-lines) * var(--body-line-height)); }

我用它来让一些图像在文章页面上溢出其容器。

我用它将可视化内容正确地放置在页面上,通过将其与填充和 vw/vh 单位结合起来。

我用它来克服 background-position 的限制,但特别是在定位渐变中的颜色停止点方面的限制。例如,“在底部短 0.75em”。

其他技巧