0%

CSS数学表达式calc()的规范草案翻译

上一篇文章关于各种像素概念和前端长度单位的理解分析了 px 的定义和各种相对长度单位,这次翻译学习一下 CSS 的 calc()规范,为下一篇做铺垫。
首先纠正 W3C 的 CSS 工作组规范草案中关于 calc()的一个错误:

纠错
实例 21 中,calc(50% + 20px) calc(50% + 20px)应该是向下和向偏移,left 应改为 right

草案翻译

W3C 的 CSS 工作组规范草案原文链接:https://drafts.csswg.org/css-values-3/#funcdef-calc

calc()函数允许使用带有加减乘除的数学表达式作为组件值。calc()表达式使用的是标准的运算符优化规则来表示其数学运算的结果。可以在表示长度、频率、角度、时间、百分比、数字或整数的任何地方使用。calc()表达式的组件值可以是文本attr()calc()表达式

实例 18:

1
2
3
4
5
6
section {
float: left;
margin: 1em;
border: solid 1px;
width: calc(100% / 3 - 2 * 1em - 2 * 1px);
}

实例 19:

1
2
3
p {
margin: calc(1rem - 2px) calc(1rem - 1px);
}

实例 20:

下面的实例设置了根元素的 font-size,所以 40em 恰好等于视窗宽度,确保无论屏幕尺寸有多大都能以数量大致相同的文本填充屏幕。

1
2
3
:root {
font-size: calc(100vw / 40);
}

如果其余的元素设计都使用 rem 单位来指定,则整个布局都会跟随视窗宽度变化。

实例 21:

下面的示例中叠加了两张背景图片,第一张图片完美居中,第二张图片在第一张的基础上向右和向下偏移 20px(原文写错)。

1
2
3
4
5
.foo {
background: url(top.png), url(bottom.png);
background-repeat: no-repeat;
background-position: 50% 50%, calc(50% + 20px) calc(50% + 20px);
}

实例 22:

下面的示例展示了如何在线性渐变中在距离两端相等(50px)的位置上放置颜色点。

1
2
3
4
5
6
7
8
9
.foo {
background-image: linear-gradient(
to right,
silver,
white 50px,
white calc(100% - 50px),
silver
);
}

语法

clac()函数的语法如下:

1
2
3
4
5
6
7
8
<calc()> = calc( <calc-sum> )
<calc-sum> = <calc-product> [ [ '+' | '-' ] <calc-product> ]*
<calc-product> = <calc-value> [ '*' <calc-value> | '/' <calc-number-value> ]*
<calc-value> = <number> | <dimension> | <percentage> | ( <calc-sum> )

<calc-number-sum> = <calc-number-product> [ [ '+' | '-' ] <calc-number-product> ]*
<calc-number-product> = <calc-number-value> [ '*' <calc-number-value> | '/' <calc-number-value> ]*
<calc-number-value> = <number> | ( <calc-number-sum> )

另外,“+”和“-”旁边需要留空格。(“\”和“/”旁边可以不用留空格)!

浏览器必须支持至少 20 个术语的表达式,其中 NUMBERDIMENSIONPERCENTAGE 都是一个术语。如果一个 calc()表达式包含了超过浏览器所支持数目的术语,那么这个表达式应该被解释为无效。

类型检查

一个数学表达式必须有他的解析类型,这个类型是 长度频率角度时间百分比数字整数之一。在表达式所在的位置这个解析类型必须是有效的,否则表达式无效。表达式的解析类型由其包含的值的类型决定,比如数字或者整数是数字解析类型,cm 是长度解析类型,deg 是角度解析类型。attr()表达式的类型是其单位参数决定的。

注意: 因为数字和整数总是被解释为数字解析类型,所以 calc ()中不支持不带单位的长度。 也就是说,即使 width: 0;width: 5px;都有效,但width: calc (0 + 5px) ;是无效的。

如果表达式上下文中接受百分比,并且表达式定义的是数字之外的类型,那么百分比符号将被定义为与表达式相同的类型。例如,在 width 属性中,百分比是长度类型。如果在上下文中,百分比值不予任何其他类型兼容,那么百分比就只是百分比类型。如果不允许百分比代替 calc(),那么包含百分比的 calc()表达式是无效的。

注意:比如在 opacity 属性中,使用 calc()时,百分比是不允许相对于数字的。因为如果允许了的话会造成代数计算时(对长度进行乘除时)出现严重问题,并且到现在为止,并没有提供新功能。(举个例子,opacity:25%;等同于 opacity:.25;,这只是一个简单的语法转变)。
注意:尽管有些属性(比如 line-heighttab-size)在使用时会将纯数字变为长度。但是在 calc()中,永远不会变成长度类型,保持数字形式。

运算符形成子表达式,子表达式根据参数获得类型。为了使表达式更简单,运算符对所接受的类型做出了限制。 在每个运算符中,都会检查左参数和右参数的类型是否兼容。 如果兼容,类型解析成如下所述(为简单起见,忽略运算符的优先规则) :

  • +”或“-”,检查两侧是否类型相同,如果相同,就解析为该类型。如果一侧数字一侧整数,解析为数字。
  • \”,检查至少一侧是数字。如果两侧都是整数,解析为整数。否则解析为另一侧的类型。
  • /”,检查右侧是否为数字。如果左侧是整数,解析为数字。否则解析为左侧的类型。

如果运算符没有通过上述检查,则表达式无效。 此外,除0无效,既包括除以文字0,也包括计算结果为0的任何数值表达式(因为纯数字表达式可以在解析时不需要任何附加信息)。

注意:代数简化不会影响 clac()表达式或者解析类型的有效性。例如 calc(5px - 5px + 10s)calc(0 \ 5px + 10s),虽然能简化但是同时运算了长度和时间,所以是无效的。

计算值

calc() 表达式的计算值是其所有分量的计算值
如果百分比在值计算时不能被解析,那么在 clac()表达式中也不能被解析。比如,calc(100% - 100% + 1em)解析为 calc(1em + 0%) ,而不是 1em。如果在计算百分比值时有特殊规则(比如 height),那么 clac()表达式中也应该应用这些规则。

实例 23:
例如,font-size 在值计算过程中计算百分比值,以便计算相对字体长度单位,而 background-position 的百分比值对布局有依赖性,因此直到使用值时才计算百分比。
因此,background-position 计算保留了 calc()中的百分比,而 font-size 将直接计算表达式并转为长度类型。

考虑到计算表格和单元格的 widthheight 时的复杂性,表格列、表格列组、表格行、表格行组以及自动和固定布局表格中的表格单元格,这些元素的 widthheight 的百分比数学表达式可能会被指定为“auto”。

范围检查

clac()表达式在值解析过程中不进行范围检查,因此超出范围的值不会导致声明无效。但是,表达式产生的值必须限制在上下文允许的范围内。在计算值时尽可能的夹紧可能的范围。如果无法充分简化表达式以允许范围检查,则最终使用值也会夹紧,(不对默认值执行夹紧)。

注意:范围检查要求接受 calc()的所有上下文将允许的值定义为闭区间

实例 24:
因为宽度不允许小于 0px,所以这三个声明是等价的:

1
2
3
width: calc(5px - 10px);
width: calc(-5px);
width: 0px;

但是请注意,width:-5px 并不等同于 width: calc(-5px)calc()范围外的值在语法上无效,并会导致删除整个声明。

序列化

在本级别中未定义 calc()的序列化。

兼容性

在 MDN 上可以看到兼容性如下:
浏览器兼容性
一个 ToB 的前端工程师就别关心兼容性了,Chrome80 真香!

总结

通过上一篇文章关于各种像素概念和前端长度单位的理解学懂了相对长度,翻译完这个草案学懂了 clac(),下一篇文章来分析在可视化大屏的布局中怎么应用相对长度单位和 calc()

👆 全文结束,棒槌时间到 👇