Calc() 的几个用例

Avatar of Chris Coyier
Chris Coyier 发布

DigitalOcean 为您旅程的每个阶段提供云产品。 立即开始使用 200 美元的免费额度!

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);
}