calc() 的作用
calc() 在 CSS 值中进行算术运算,而且可以混合使用那些只有在布局时才确定大小的单位。最经典的场景是“全部宽度减去一个固定宽度的侧边栏”:
100% 只有在页面完成布局后才能确定,而 180px 是固定的,所以没有任何一种单位能单独表达这个值。calc() 会把它算出来:在 800px 宽的预览中,这一行宽 784px,主栏得到 604px,布局下方的那一行会打印出真实的数字。如今用 flexbox 或 grid 可以不做数学运算就实现这种布局,但同样的减法仍然随处可见:高度减去头部、宽度减去间距。
语法规则
width: calc(100% - 2rem); /* + and - need spaces on both sides */
width: calc(100% / 3); /* divide by a plain number */
margin: calc(var(--space) * 2); /* multiply by a plain number */
height: calc(100vh - (64px + 1rem)); /* parentheses group */
| 规则 | 有效 | 无效 |
|---|---|---|
+ 和 - 两侧要有空格 | calc(100% - 20px) | calc(100%-20px) |
*:至少一侧是纯数字 | calc(2 * 1rem) | calc(2px * 3px) |
/:长度除以纯数字 | calc(100% / 3) | width: calc(300px / 3px)(结果不是长度) |
| 单位必须兼容 | calc(1rem + 10px) | calc(1rem + 2s) |
之所以有空格规则,是因为 100% -20px 会被解读为两个独立的值,第二个是负数。当 calc() 无效时,整条声明都会被丢弃,元素保留之前规则给它的值。
试一试表达式
为盒子的宽度输入任意表达式。浏览器会告诉你它是否有效,以及最终解析出的宽度:
试试 100%-200px(无效,没有空格)、100% / 3、50% + 4rem、2 * 120px、200px * 2px(无效)和 (100% - 40px) / 2。
calc() 配合 CSS 变量
变量和 calc() 是天生的搭档:只存储一次基础值,其他值都从它推导出来。没有单位的变量乘以一个单位后,就变成了长度:
把 --space 改成 12px,或者把 --cols 改成 4,所有推导出的值都会跟着变。如何声明变量以及变量的作用域,见 CSS 变量页面。
常见用法
| 需求 | calc() |
|---|---|
| 填满固定宽度元素旁边的空间 | width: calc(100% - 240px) |
| 固定头部下方的区域 | min-height: calc(100vh - 64px) |
| 一半宽度减去一半间距 | width: calc(50% - 8px) |
| 让一个 100px 的绝对定位盒子居中 | left: calc(50% - 50px) |
| 随屏幕略微变大的字号 | font-size: calc(1rem + 0.5vw) |
| 把数字变量变成长度 | calc(var(--n) * 1px) |
| 取变量的负值 | calc(var(--space) * -1) |
如果一个值必须保持在某个范围内,请使用 clamp(),它的参数里可以直接写同样的运算,不用再写 calc()。
calc() 可以用在哪里
凡是允许使用长度、百分比、数字、角度或时间的地方都可以:width、margin、padding、top、font-size、line-height、网格轨道尺寸、transition-delay,以及 transform 中的角度。它可以嵌套,嵌套的 calc() 和普通的括号作用相同。
角度就是一个很好的例子。下面每个刻度都有自己的 --i(0 到 11),一条规则就能用 calc(var(--i) * 30deg) 把它变成角度:
第 3 个刻度得到 rotate(90deg),Chrome 把它报告为矩阵 matrix(0, 1, -1, 0, 0, 0):也就是旋转四分之一圈。left: calc(50% - 2px) 减去刻度自身 4px 宽度的一半,让每个刻度居中。
常见错误
-或+两侧没有空格。calc(100%-20px)无效,会被忽略。- 两个长度相乘。
calc(10px * 2px)在 CSS 中没有意义。*的一侧必须是纯数字。 - 父元素没有设置高度时,用 calc() 计算百分比高度。 和
height: 100%一样,height使用calc(100% - 20px)时需要父元素有确定的高度。 - 在 flexbox 或 grid 就能搞定的地方使用 calc()。 用
width: calc(33.333% - 10px)来做分栏很脆弱;display: grid; grid-template-columns: repeat(3, 1fr); gap: 15px则完全不需要计算。 - 以为会看到错误信息。 浏览器不会对无效的 calc() 发出警告。请在开发者工具中检查元素:无效的声明会显示为被划掉的样子。
常见问题
CSS 中的 calc() 有什么作用?
它根据包含 +、-、* 和 / 的表达式计算出一个值,并且可以混合使用只有在布局时浏览器才知道具体大小的单位。width: calc(100% - 200px) 表示父元素的全部宽度减去 200px,不管父元素有多宽。
为什么我的 calc() 不生效?
最常见的原因是缺少空格:+ 和 - 两侧必须有空白,所以 calc(100%-20px) 是无效的。其他原因还有:两个长度相乘(10px * 2px),或者除以一个长度,这样得到的都不是长度。无效的 calc() 会让整条声明失效,于是浏览器会忽略它。
calc() 中可以使用 CSS 变量吗?
可以。calc(var(--gap) * 2) 把变量翻倍,calc(var(--columns) * 1px) 把一个没有单位的数字变成像素值。浏览器会先代入变量的值,再进行计算。
calc() 中的 * 和 / 两侧需要空格吗?
不需要。只有 + 和 - 要求有空格,因为没有空格的话,-20px 会被当成一个负数。* 和 / 两侧的空格可有可无,但所有运算符两侧都加空格是最容易记住的规则。
calc() 和 clamp() 有什么区别?
calc() 根据一个表达式计算出一个值。clamp(MIN, VAL, MAX) 把一个值限制在两个界限之间。两者可以组合使用:clamp() 的每个参数本身就可以包含 calc 风格的运算,比如 clamp(1rem, 1rem + 2vw, 3rem)。