calc()
是一种原生 CSS 方法,可以在 CSS 中进行简单的数学运算,以替换任何 长度值(或几乎任何数值)。 它有四个简单的数学运算符:加(+)、减(-)、乘(*) 和除(/)。 能够在代码中进行数学运算非常方便,并且是对这种相当依赖数字的语言的受欢迎补充。
但它有用吗? 我过去曾绞尽脑汁地试图找出明显有用的案例。 不过确实有一些。
预处理器不能做我们的数学运算吗?
所有 CSS 预处理器都具有数学函数,它们非常有用。 但它们并不像原生数学运算那样强大。 calc()
最有用的功能是它能够混合单位,例如百分比和像素。 任何预处理器都无法做到这一点。 这是必须在渲染时发生的事情。
语法
.thing {
width: 90%; /* fallback if needed */
width: calc(100% - 3em);
}
数学运算符周围必须有空格。 你可以嵌套。
浏览器支持
它出奇地好。 Can I use… 始终非常适合检查那里的详细信息。 在桌面端,需要关注的是 IE 9+、Safari 6+,并且在 Opera 15+ 上切换到 Blink 之前不会出现。 在移动端,Android 和 Opera Mini 尚未支持它,而 iOS 仅在 6.0+ 上支持。
你必须在那里做出决定。 我已经能够在某些特定场景中实际将其用于生产环境了。
用例 #1: (所有高度 - 标题)
具有 height: 100%
的块级子元素的高度与其块级父元素的高度相同。 在某些情况下,使彩色模块与父元素一样高可能很有用。

但是现在假设父元素变得太小而无法容纳模块中的所有内容。 你希望内容滚动,但你只希望内容滚动,而不是整个模块。 只需设置 overflow-y: auto;
对吧? 不完全是,因为只有当内容元素本身具有可以溢出的设置高度时,overflow-y 才有用。 我们不能使内容元素的高度为 100%,因为有了标题,这将太高。 我们需要 100% 减去 标题的高度。 如果我们知道标题的高度,这就可以实现了!

* {
/* So 100% means 100% */
box-sizing: border-box;
}
html, body {
/* Make the body to be as tall as browser window */
height: 100%;
background: #ccc;
padding: 20px;
}
body {
padding: 20px;
background: white;
}
.area-one {
/* With the body as tall as the browser window
this will be too */
height: 100%;
}
.area-one h2 {
height: 50px;
line-height: 50px;
}
.content {
/* Subtract the header size */
height: calc(100% - 50px);
overflow: auto;
}
你可能会抱怨标题不应该具有固定大小。 如果将来 calc()
可以减去元素的测量大小,那可能会很酷,但这目前还不可能。 你可以将标题设置为溢出 带省略号。
Check out this Pen!
用例 #2: 距右下角 X 像素
我们可以轻松地将背景图像定位在距左上角 X 像素的位置。
background-image: url(dog.png);
background-position: 50px 20px;
这会将小狗放置在元素框左侧 50px 和顶部 20px 的位置。 但是,如果你想将其放置在右侧 50px 和底部 20px 的位置呢? 仅使用直线长度值是不可能的。 但是 calc()
使其成为可能!
background-image: url(dog.png);
background-position: calc(100% - 50px) calc(100% - 20px);
Check out this Pen!
用例 #3: 无需父元素的固定间距
假设你想要两个并排的列。 第一个宽度为 40%,第二个宽度为 60%,但列之间有 1em 的固定间隙。 不要过度思考网格 具有固定间距,但在某种意义上,它们不是真正的间距。 列本身彼此紧贴,并且列是由这些列内部的内部填充创建的。
使用 calc()
,我们可以使第一列宽度为 40% 且右侧边距为 1em,然后使第二列宽度为 60% 减去该 1em。
.area-one {
width: 40%;
float: left;
margin-right: 1em;
}
.area-two {
width: calc(60% - 1em);
float: right;
}
如果你想保持比例更准确,可以从两侧移除一半的间距。 现在,你有了两个由固定空间分隔的真正列,而无需使用父元素或内部填充。
Check out this Pen!
用例 #4: 显示数学运算更容易理解
说到列,有时除法运算会变得很混乱。 假设你想要一个 7 列网格,你可能有如下类:
.column-1-7 {
width: 14.2857%
}
.column-2-7 {
width: 28.5714%
}
.column-3-7 {
width: 42.8571%
}
不完全是 幻数,但乍一看很难理解。
.column-1-7 {
width: calc(100% / 7);
}
.column-2-7 {
width: calc(100% / 7 * 2);
}
.column-3-7 {
width: calc(100% / 7 * 3);
}
Check out this Pen!
用例 #5: 糟糕的 box-sizing 替代方案
我非常喜欢 通用 box-sizing: border-box;
,因为它意味着你无需进行太多数学运算即可确定元素的实际大小,或者在边框和填充发生变化时调整这些数学运算。
如果你想复制 box-sizing 的功能,可以使用 calc()
根据需要减去这些值。
.module {
padding: 10px;
/* Same as box-sizing: padding-box */
width: calc(40% - 20px);
border: 2px solid black;
/* Same as box-sizing: border-box */
width: calc(40% - 20px - 4px);
}
box-sizing 的浏览器支持远优于 calc()
,因此这种情况很少使用。
未来?
我认为当我们能够在 attr() 函数中使用除 content 属性之外的其他位置时,这将很有趣。 通过这种方式,我们可以从 HTML 元素中提取值,对其进行计算,并使用新数字来执行设计相关操作。 例如,根据包含的数字对输入进行着色。
也许我们甚至可以利用它对 <progress>
元素执行一些花哨的操作,例如将其变成类似于 此页面 上的速度计。 可能类似于
/* Not real */
progress::progress-bar {
transform: rotate(calc(!parent(attr(value))*18)) + deg);
}
我发现 calc 最不直观的一点是,100% 似乎始终与 100VH(或 100VW)相同,但文档中通常未对此进行确认。 对我来说
.foo {
width: calc(100%-3em);
}
是其直接父元素的 100%,而不是窗口。 我担心 calc 将继续价值有限,直到我们能够说 calc(#bar(width) -3em); 并知道它会起作用。
我认为它没有被记录在案,因为它不正确。 只要父元素具有定义的宽度(并且你的元素不是固定的),它将始终相对于其父元素。 例如:http://codepen.io/ggilmore/pen/d8fc7309e1fc387f8965e66674ac031c
谢谢。 看,这很有道理,我认为这是我第一次理解它。 即使在我撰写最初的帖子时,我也在质疑自己。 有可能是我自己的先入为主的想法,但我仍然不知道我是否遇到过像你的回复一样简单的句子。 不过,是“定义的宽度”还是“定义的相对尺寸”(例如,根据 Chris 的原始示例,height: calc(100% – 30px) 似乎表明它会测量高度而不是宽度)。
是的,我的意思就是这样。只说宽度是因为示例的原因。
一篇优秀且内容丰富的文章……我曾向学生教授过 calc(),但一直苦于找不到好的用例……您上面列出的用例非常棒……感谢分享……
我只是希望移动端的支持更好……但随着时间的推移,这种情况会得到改善……
以前甚至不知道 calc(),真希望几年前就知道这个!它可以节省很多时间!感谢发布这篇文章!
这很好,Chris。我认为使用 calc 实际上并不能获得您所需的页面精确尺寸,因为它会太大。有没有办法使这三个页面相等。
谢谢。
Tab Atkins 通过 Lea Verou 关于 calc() 和 attr() 的文章 表示,CSSWG 已同意在 calc() 中允许使用 attr(),所以现在只是时间问题了?
能够根据属性值创建简单的条形图而无需使用 js 将非常不错。
有人知道使用 calc() 是否会带来任何性能影响吗?快速谷歌搜索几乎没有找到相关信息。
这真的很有帮助。
感谢分享
好吧,这些用例可以用旧技术实现。
值得注意的是,对于 #2,您可以 使用关键字 指定偏移量是从哪个边缘给出的,这要感谢 CSS 背景和边框模块第 3 级
有趣的是,Safari 6.0 不支持您提到的背景偏移语法,因此您可以使用 calc 来获得更好的支持。太棒了!
https://caniuse.cn/background-img-opts
是的,这种定位是在 2012 年底在 Webkit 中实现的:。对 Webkit 来说真是耻辱。参见 错误 37514。
嗨,在用例 #3 中,您还必须减去第二个区域的填充,因为盒模型的原因;它看起来像这样
width: calc(60% – 1em – 4 * 30px);
再见
像往常一样,这是一篇很棒的文章,Chris,不过我有一个问题。
关于向后兼容性,您会像这样处理网格替换示例吗?
.column-2-7 {
width: 28.5714%;
width: calc(100% / 7*2);
}
我知道有些人可能会说这是浪费时间(和字节),但它可能是消除(几乎)魔法数字的好方法。也许毫无意义?
也可以直接注释它
.column-2-7 {
width: 28.5714%; /* (100% / 7*2) */
}
哦,哇,我肯定偶尔需要用例 #2,每次遇到这种情况,我都会完全忽略 calc..
所以如果我想让背景图片居中但偏移……例如,我可以使用“calc(centered – 50px)”吗?或者严格使用单位值(50% / 50px)。
我想以后会玩一玩。
需要一点帮助。可以使用 calc 创建粘性页脚吗?
理论上可以,据我了解,前提是页脚的父元素的高度等于页面高度,否则您的公式会变得复杂。
但是,您不能简单地使用
position: absolute; bottom: 0px;
相对于页脚 div 来实现相同的目标吗?
我最大的失望在于,刚刚读完这篇文章后,我发现 Android 和其他移动浏览器平台对“calc”的支持不足。
我想使用 calc 相对定位一个切换样式的 div,其中包含一个登录表单,以便它能够响应并显示在 y 轴的中心。
然后我只需要通过 @media 查询控制其每个断点的最大宽度(以百分比表示),以及字体、行高、填充和边距参数。我想我也必须以同样的方式处理 y 轴定位——至少目前是这样。
就像 javascript 函数,数学计算。
用例 #2 可以不使用
calc()
来实现。要实现演示中所做的操作,您可以执行以下操作演示:http://codepen.io/skimberk1/pen/jdbli
嗨,
Chris,你真是天才,你解决问题就像涂黄油一样轻松。我试图在设计领域模仿你。而且我做得很好。谢谢。
用例 #2 太棒了。
我想更多地使用此功能,但我担心性能成本……有人对此有任何数据吗?
支持程度比我最初预期的要高……其他人是否也看到了一个阶段,届时支持和最低浏览器版本将被全球采用,从而形成开发人员支持和改进的基础?
好吧,它很有用,但不是特别重要。能够以这种方式将 calc 与变量混合会很好
或者
为不支持的浏览器编写 polyfill 有多困难?值得这样做吗?
如果我的记忆没错,calc() 中的 100% 似乎反映了最后一个 position:relative 父元素的宽度。
至少这是我目前所记得的,所以不要把它当作真理,而只是某些人可能需要考虑的可能性。由于从服务器启动参数到 CSS 以及介于两者之间的一切代码都出现在我的工作中,因此有时很难准确地记住所有内容。 :)
calc() 函数在这里立即受到欢迎,尽管我必须承认,有一些小问题我希望它能包含在内,例如一些条件语法
至于 attr() 值和超出 content 属性的广泛使用,在这种情况下,人们可能会遇到实例的特殊性。在此处(定期)使用时,通常需要添加一个额外的 CSS 规则,该规则将包含 attr() 的规则的选择器与唯一标识页面中要应用效果的每个元素的选择器合并。
原因很简单,因为每个实例都可能具有唯一的最终值,因此每个实例都需要 CSS 规则中一个唯一的插槽来保存该值,而不是通常寻求的类的共享值。
当人们尝试实时修改动画关键帧和类似活动时,他们经常会遇到这种实例化行为,但一旦想到如果值发生变化,它就需要通过某种方式拥有自己的规则插槽,那么解决方法就非常简单了。
人们通常会觉得需要重新应用规则才能使其连接,但实际上,首先拥有那个单独的规则插槽似乎是最重要的。
这是一种为具有“outline”属性的元素设置高度的绝佳方法。否则,除非您指定边距,否则轮廓会滑入附近的元素。感谢您发布此内容!它真的很棒!
为用例 #2 点赞。这是我们能够使用它的最佳用途!
Calc()
还可以消除对“内部包装器”类型元素的需求(例如,具有固定宽度内容的 100% 宽度背景),方法是将padding: 0 calc(50% - sitewidth / 2)
应用于父元素。示例 此处。这显然与
box-sizing: border-box
不兼容,但您可以像示例中那样将该特定元素恢复为content-box
。我的意思是,如果可能,谁不希望这样呢?
而不是这样?
方案2是一个巨大的胜利。
我最初的想法是这将是border-box的一个很好的替代方案,但正如你所说,浏览器兼容性有点压倒了这种可能性。
我想用它来进行垂直居中。我将一个图像放在一个固定高度/宽度的包装器中,并尝试执行以下操作
Firefox和Chrome都将其解释为
-25%
。并且它们是根据父元素的宽度而不是高度来进行计算的。所以,它似乎不适用于该应用场景。有没有更好的解决方案**使用calc()**?似乎它将是添加额外包装器和使用负边距的绝佳替代方案。
你的示例在Firefox中都不能正常工作,因为它仍然不支持“box-sizing”属性的无前缀版本。在你的“*”规则中添加“-moz-box-sizing: border-box”可以解决此问题。
我使用calc()设置了一个具有固定宽度间距的响应式网格,如果你将“$siteWidth: 960px;”更改为你想要的宽度,列的宽度将发生变化,但间距将保持不变。我真正期待的是将calc()与CSS变量一起使用。想象一下,更改网格而无需每次使用媒体查询重新声明它。只需更改变量,整个网格就会发生变化。有点酷。
这是一个真实的例子。我为一个当地的蜂蜜摊制作了一个单页网站,我用它来试验一些较新的CSS功能
http://beeladieshoney.com/
主要内容
DIV
使用CSS3多背景(蜂窝图案、底部草地和底部蜜蜂)。为了定位蜜蜂,我使用calc
属性计算到底部,然后向上25像素。百分比效果不太好,因为随着内容量的变化和其它因素的影响,百分比会发生变化,而固定的像素数也不适用于相同的原因。我仍在进行一些跨浏览器测试,但我认为我已经设置了正确的回退方案。另一方面,我应用的动画只在Firefox中有效。好吧,它在Chrome中也通过
-webkit-
前缀生效,但在Safari中失效,所以我不得不删除所有-webkit-
前缀,从而不幸地也禁用了Chrome中的动画。它应该开始在任何更新到不再需要我之前使用的前缀的浏览器中生效。