CSS 计算属性 calc()的完整指南(下)

从之前的文章:CSS 计算属性 calc()的完整指南(一),我们可以学习到几个方面:

  1. calc() 只作用于属性值
  2. calc() 用于长度和其他数值
  3. 不能在媒体查询中使用
  4. 混合单位
  5. 与预处理器数学比较

本文接着之前未完的工作,恰好是周末,一次把它完成。如果我的工作对你有帮助,希望可以点个赞和收藏分享。

显示数学

即使你没有使用只有calc()才能实现的功能,也可以用它在CSS里面 “展示你的工作”。例如,假设你需要精确计算一个元素的1⁄7的宽度……。

.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 {
  /* 有效? */
  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) ),但这是一个例子,说明空格不仅是必需的,而且是有用的。

Tab Atkins告诉我,+和-周围需要间隔的原因其实是出于解析的考虑。我不能说我完全理解,但例如,2px-3px被解析为数字 “2 “和单位 “px-3px”,这对任何人都没有好处,而+还有其他问题,比如被 “数字语法消耗”。我本来猜测空格应该是和自定义属性的–语法有关,但没有!

乘法和除法不需要运算符周围的空格。但我认为好的一般建议是,为了其他运算符的可读性和肌肉记忆,应该包括这些空格。

外围的空白并不重要。如果你愿意,你甚至可以做换行符。

.el {
  /* 有效的 ? */
  width: calc(
    100%     /   3
  );
}

不过,请注意以下事项:calc() 与开头括号之间没有空格。

.el {
  /* 无效的 ? */
  width: calc (100% / 3);
}

嵌套计算(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()的唯一作用是与内容属性结合的字符串。这意味着这个可以用:

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 – Rules

如果您需要找出计算值,有一个计算选项卡(在所有浏览器的DevTools中,至少在我所知道的范围内)会向您显示它。

Chrome DevTools – Computed

浏览器支持

这个浏览器支持数据来自Caniuse,它有更详细的数据。数字表示浏览器支持该版本及以上的功能。

如果你真的需要支持超远期(如IE 8或Firefox 3.6),通常的技巧是在使用calc()的属性或值之前再添加一个属性或值。

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

calc()也有不少已知的问题,但都是针对旧浏览器的。canIuse……列出了13个,下面是一小部分。

  • Firefox <59 不支持color函数的calc()。例如:color: hsl(calc(60 * 2), 100%, 50%)
  • 当calc()用于任何一个值时,IE 9 – 11将不会渲染盒影属性。
  • IE 9 – 11和Edge都不支持表格单元格上的width: calc()。

用例方

我问了一些CSS开发者最后一次使用calc()是什么时候,这样我们就可以在这里品味一下其他人在日常工作中是如何使用它的。

  • 我用它创建了一个.full-bleed 实用工具类:.full-bleed{width:100vw;margin left:calc(50%-50vw);}我想calc()在我的前3个CSS中。
  • 我用它来为粘性页脚腾出空间。
  • 我用它来设置一些流体字体/动态排版……根据最小值、最大值和视口单位的变化率来计算字体大小。不仅仅是字体大小,还有行高。
  • 如果你使用calc()作为流体字体情况的一部分,涉及到视口单位等,确保你包含一个使用rem或em的单位,这样用户仍然有一些控制权,通过放大或缩小他们需要的字体。
  • 我很喜欢的一个方法是有一个 “内容宽度 “的自定义属性,然后用它来创建我需要的间距,比如 margins: .margin { width: calc( (100vw – var(–content-width)) / 2); }
  • 我用它创建了一个跨浏览器的drop-cap组件。下面是它的一部分。.drop-cap { –drop-cap-lines: 3; font-size: calc(1em * var(-drop-cap-lines) * var(-body-line-height)); }。
  • 我用它来使文章页面上的一些图片溢出其容器。
  • 我用它与padding和vw/vh单位相结合,在页面上正确地放置了一个可视化。
  • 我用它来克服背景-位置的限制,但特别是在渐变中定位颜色停止的限制。比如 “在底部少停0.75em”。

其他技巧

  1. 一个两层网格,可以分成一列,而无需媒体查询
  2. 一个纵横比的头部组件
  3. 增强高对比度的颜色
  4. 帮助解决基于百分比的剪辑路径的坐标问题

以上就是这篇文章的全部内容了,读下来收获很大,没想到calc()属性还有这么多限制和作用的。


关注我

我的微信公众号:前端开发博客,在后台回复以下关键字可以获取资源。

  • 回复「小抄」,领取Vue、JavaScript 和 WebComponent 小抄 PDF
  • 回复「Vue脑图」获取 Vue 相关脑图
  • 回复「思维图」获取 JavaScript 相关思维图
  • 回复「简历」获取简历制作建议
  • 回复「简历模板」获取精选的简历模板
  • 回复「加群」进入500人前端精英群
  • 回复「电子书」下载我整理的大量前端资源,含面试、Vue实战项目、CSS和JavaScript电子书等。
  • 回复「知识点」下载高清JavaScript知识点图谱

每日分享有用的前端开发知识,加我微信:caibaojian89 交流