CSS 中的 ∞(无穷)你知道多少
众所周知,很多编程语言都提供了两个特殊的数值,用以表示无穷大和无穷小。例如,在JavaScript中,我们使用 Infinity
关键词表示无穷大(∞),而 -Infinity
表示无穷小(-∞)。然而,或许你会感到意外的是,CSS 中同样存在这两个概念——无穷大(infinity
)和无穷小(-infinity
)。当我第一次得知这个信息时,确实让我感到十分惊奇。这个发现使我觉得 CSS 变得更加神奇有趣。
那么,今天我就和大家一起来聊聊 CSS 的这个常量无穷大(小)!
故事从这里开始...
使用 CSS 给元素定义样式时,有很多场景都会用到一个极大的值。例如:
前段时间,有一位网友提到一个相似的问题,定位元素 top
属性的最大值是多少?能否给它设置一个无穷大的值?刚开始,我认为像 99999999vw
这样的值足够了。但后来一想,这样似乎不够严谨。
为了获得一个更严谨的答案,我发现 W3C 的 CSS 的值和单位 (CSS Values and Units Module Level 4)规范中为 CSS 定义了无穷大(infinity
)和无穷小(-infinity
):
CSS 中无穷的定义
When a calculation or a subtree of a calculation becomes
infinite
orNaN
, representing it with a numeric value is no longer possible. To aid in serialization of these degenerate values, the following additional math constants are defined:
简而言之,当计算的值无法用数字表示时,它可能变为无穷大、无穷小或 NaN
。为了序列化这些退化值,W3C 的 CSS 工作小组定义了以下额外的数学常量:
-
infinity
(无穷大): 表示正无穷大的值(+∞),它表示最大可能的值 -
-infinity
(无穷小): 表示负无穷小的值(−∞),它表示最小可能的值,与calc(-1 * infinity)
的结果等同 -
NaN
(非数值): 表示无定义值
需要注意的是,所有这些关键词都属于 <number>
类型的值。
与一般 CSS 关键词规则相同,这些关键词不区分大小写。因此,calc(InFiNiTy)
是合法的,但 NaN
必须使用规范规定的大小写进行序列化。接下来我们将重点讨论 CSS 中的无穷,而不涉及 NaN
。
在深入讨论之前,有一个重要的基本规则:无穷大( infinity
)和无穷小( -infinity
)只能在 calc()
函数中使用。此外,它们是 <number>
值类型,因此要获得无穷大的长度值,需要使用类似 calc(infinity * 1px)
的表达式。
CSS 中哪些计算值会是无穷大
正如前文所述,CSS 中的正无穷大(+∞
)和负无穷小(−∞
)可以直接使用数学常量 infinity
和 -infinity
编写。此外,CSS 还可以通过其他方式获得这些特殊值,例如某些计算的结果可能会产生无穷大或无穷小。
注意:下面关于产生 NaN
的规则覆盖了上述产生无穷大的规则。
NaN
(非数值)是某些没有明确定义值的操作的结果。它可以直接使用数学常量 NaN
编写,也可以作为某些计算的结果产生:
例如,calc(-5 * 0)
产生一个无符号零,即计算解析为 0⁻
,但由于它是一个顶级计算,因此它会被审查为无符号零。另一方面,calc(1 / calc(-5 * 0))
产生 −∞
,与 calc(1 / (-5 * 0))
相同,即内部计算解析为 0⁻
,因为它不是顶级计算,所以它保持不变传递到外部 calc
以产生 −∞
。如果它被审查为无符号零,它将产生 +∞
。
虽然有这么多种方式可以在 CSS 中获得无穷大或无穷小,但我仍然建议直接使用 infinity
或 -infinity
,这样能更清晰地表达意图,而不是在样式表中放入一个巨大的数字(或数字值),以使 CSS 更加清晰。
CSS 中无穷大的用例
CSS 无穷大(infinity
)最典型的用例应该是 z-index
。如果将 z-index
的值设置为 infinity
,你将获取最大可能值,再也不用担心别人设置值胜过你。例如下面这个示例,无论你在蓝色的卡片上设置多大的 z-index
值,它都无法用于设置了 z-index: calc(infinity)
的紫色卡片:
<div class="blue"></div>
<div class="purple"></div>
.blue {
--z-index: 999999999;
z-index: var(--z-index);
}
.purple {
z-index: calc(infinity);
}
无论你在输入框中输入多少个 9
,最终无法胜过紫色卡片在 z
轴上的层级:
Demo 地址:codepen.io/airen/full/…
你可曾知道,z-index
的上限值是 2147483647
。那么当 z-index
属性值 infinity
遇到其上限值 2147483647
时,又谁将胜出呢?这是一个意思的问题。
通常情况之下,z-index
属性的 infinity
和 2147483647
值被视为相同的值,但无法确定 calc(infinity)
计算的值就等于 2147483647
。我们写个测试案例来验证它。
<div class="parent">
z-index: 2;
<div class="blue">z-index: infinity</div>
</div>
<div class="purple">z-index: 3</div>
.parent {
z-index: 2;
.blue {
z-index: calc(infinity);
}
}
.purple {
z-index: 3;
}
Demo 地址:codepen.io/airen/full/…
继续回到 z-index
身上。z-index
还有另一个规则:“在相同的堆叠环境当中,如果两元素指定的 z-index
相同,那么将根据元素在 HTML 源码中出现的顺序来决定,即先出现的先胜。换句话说,如果我们的假设是成功的(infinity
比 2147483647
大),则无论 HTML 的顺序如何,它都应该位于顶部。
因此,我准备了两个堆栈上下文,如下所示,每个堆栈上下文都有不同的 HTML 顺序:
<div class="wrapper">
<div class="blue">z-index: 2147483647</div>
<div class="purple">z-index: calc(infinity)</div>
</div>
<div class="wrapper">
<div class="purple">z-index: calc(infinity)</div>
<div class="blue">z-index: 2147483647</div>
</div>
.wrapper {
z-index: 0;
}
.blue {
z-index: 2147483647;
}
.purple {
z-index: calc(infinity);
}
将 .blue
和 .purple
放在相同的堆叠上下文环境中进行比较。如果 z-index
的 infinity
和 2147483647
相同,则按照 HTML 的顺序来决定谁胜出;如果是 infinity
大于 2147483647
,则设置了 calc(infinity)
元素(.purple
)始终将位于 z
轴的顶部。
Demo 地址:codepen.io/airen/full/…
结果已经告诉我们答案了,z-index
的值 infinity
和 2147483647
相等。但这并不能说 infinity
就等于 2147483647
。听起来有点拗口,甚至晕乎。你可以尝试一下,calc(infinity - 2147483647)
结果和 2147483647
也一样。这就意味着它已经停止在 z-index
限值处。
这个案例从侧面又说明,calc(infinity)
不一定始终能让 z-index
胜出,只不过它的上限值 2147483647
并不广为人知,而且也很难记得住这一串数值。也就是说,当有一天,你将 z-index
属性的值设置为 calc(infinity)
,它并没有胜出的话,你需要知道其中的原委,并做出正确的选择。
另一个典型用例就是胶囊按钮或类似于胶囊外形的 UI:
以按钮为例,我习惯性会将 border-radius
设置为 999rem
或 999vmax
:
.pill {
border-radius: 999vmax;
}
这样做不管元素高度是多少,都可以实现胶囊 UI 的效果:
Demo 地址:codepen.io/airen/full/…
你现在可以将 999vmax
替换成 calc(1px * infinity)
:
.pill {
border-radius: calc(1px * infinity);
}
Demo 地址:codepen.io/airen/full/…
另一个相似的用途可以用于隐藏文本或隐藏元素中。例如在视觉上隐藏元素(仅供屏幕阅读器可见)的代码片段:
.sr-only {
position: absolute;
width: 1px;
height: 1px;
padding: 0;
margin: -1px;
overflow: hidden;
clip: rect(0, 0, 0, 0);
white-space: nowrap;
border-width: 0;
left: -9999vmax;
}
代码中的 left: -9999vmax
可以使用 calc(-1px * infinity)
来替代:
.sr-only {
/* ... */
left: calc(-1px * infinity);
}
从功能上说,没有啥区别。最终效果是相同的。但我认为它确实有助于使代码更具可读性,因为无穷大传达了真正的意图。
最后再来看一个与动画相关的案例。假设你有一个元素要从屏幕上移出,往往我们会像下面这样来定义动画:
@keyframes slideOutRight {
to {
translate: 100vw;
}
}
.ani {
animation: slideOutRight 1s ease-out infinite;
}
Demo 地址:codepen.io/airen/full/…
要是你想着将 @keyframes
中的 translate
属性值替换为 calc(1px * infinity)
,你会发现元素将立即跳到动画的最后并保持在那里:
Demo 地址:codepen.io/airen/full/…
丝毫感觉不到动画的效果。这是有道理的。在通往无穷大的路上没有增量值。无穷大的一部分仍然是无穷大。因此,在动画的每一帧中,动画值都是无穷大。
@media (prefers-reduced-motion: reduce) {
*,
::before,
::after {
animation-delay: -1ms !important;
animation-duration: 1ms !important;
animation-iteration-count: 1 !important;
background-attachment: initial !important;
scroll-behavior: auto !important;
transition-duration: 0s !important;
transition-delay: 0s !important;
}
}
有了无穷大这个常量之后,你又可以为减少运动添加一个更简单的方法。你只需要将 transition-delay
和 animation-delay
的值设置无穷大,那么页面上的动画就永远不会开始播放:
@media (prefers-reduced-motion: reduce) {
*,
::before,
::after {
animation-delay: calc(1s * infinity) !important;
transition-delay: calc(1s * infinity) !important;
}
}
小结
简单地的小结一下,CSS 中的无穷大,主要要知道的是它本质上是表示特定情况下最大可能值或最小可能值的简写。在一些特定的情景中,它们非常有用,既能帮助我们实现想要的效果,又传达了 CSS 真正的意图。
到目前为止,该属性值已得到了很好的支持,但是否在项目中使用它,这取决于你。最后,我想再强调一次的是,无穷大可以作为一种表达意图的良好指示,使你的 CSS 更易读,但这当然不是强制性的。
转载自:https://juejin.cn/post/7344573755643084854