CSS 中有随机数吗?

Avatar of Alvaro Montoro
Alvaro Montoro

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

CSS 允许您在 Web 上创建动态布局和界面,但作为一种语言,它是静态的:一旦设置了值,就无法更改。随机性的概念不在考虑范围内。在运行时生成随机数是 JavaScript 的领域,而不是 CSS。或者是吗?如果我们考虑一点用户交互,我们实际上可以在 CSS 中生成一定程度的随机性。让我们来看看!

来自其他语言的随机化

正如 Robin Rendle 在 CSS-Tricks 上的一篇文章中解释的那样,有一些方法可以使用 CSS 变量来获得一些“动态随机化”。但这些解决方案并非 100% CSS,因为它们需要 JavaScript 使用新随机值更新 CSS 变量。

我们可以使用 Sass 或 Less 等预处理器来生成随机值,但是一旦 CSS 代码编译并导出,这些值就会固定,随机性就会丢失。正如 Jake Albaugh 解释的那样

为什么我在乎 CSS 中的随机值?

过去,我开发过一些简单的纯 CSS 应用程序,例如 琐事游戏Simon 游戏魔术。但我想做一些更复杂的事情。关于创建这些纯 CSS 代码段的有效性、实用性或实用性的讨论,我将在以后进行。

基于一些棋盘游戏可以表示为有限状态机 (FSM) 的前提,可以使用 HTML 和 CSS 来表示它们。因此,我开始开发一个 蛇梯游戏 (aka Chutes and Ladders)。这是一个简单的游戏。目标是通过避开蛇并试图爬上梯子,将棋子从起点推进到棋盘的终点。

这个项目看起来可行,但我缺少了一些东西:**掷骰子**!

掷骰子(以及抛硬币)普遍被认为是随机化的。你掷骰子或抛硬币,每次都会得到一个未知的值。

模拟随机掷骰子

我打算叠加带有标签的图层,并使用 CSS 动画来“旋转”和交换哪个图层位于顶部。类似于这样

浏览器上图层动画的模拟

模拟这种随机化的代码并不复杂,可以通过动画和不同的动画延迟来实现

/* The highest z-index is the numbers of sides in the dice */ 
@keyframes changeOrder {
  from { z-index: 6; } 
  to { z-index: 1; } 
} 

/* All the labels overlap by using absolute positioning */ 
label { 
  animation: changeOrder 3s infinite linear;
  background: #ddd;
  cursor: pointer;
  display: block;
  left: 1rem;
  padding: 1rem;
  position: absolute;
  top: 1rem; 
  user-select: none;
} 
    
/* Negative delay so all parts of the animation are in motion */ 
label:nth-of-type(1) { animation-delay: -0.0s; } 
label:nth-of-type(2) { animation-delay: -0.5s; } 
label:nth-of-type(3) { animation-delay: -1.0s; } 
label:nth-of-type(4) { animation-delay: -1.5s; } 
label:nth-of-type(5) { animation-delay: -2.0s; } 
label:nth-of-type(6) { animation-delay: -2.5s; }

动画速度已放慢,以便于交互(但仍然足够快以查看下面解释的障碍)。伪随机性也更清晰。

查看 CodePen 上 Alvaro Montoro (@alvaromontoro) 编写的
使用 CSS 生成伪随机数的演示

CodePen 上。

但随后我遇到了一个障碍:我得到了随机数,但有时,即使我点击我的“骰子”,它也没有返回任何值。

我尝试增加动画时间,这似乎有所帮助,但我仍然遇到了一些意外的值。

那时,我做了大多数开发人员在遇到无法仅通过在线搜索解决的障碍时所做的事情:我以 StackOverflow 问题 的形式向其他开发人员寻求帮助。

幸运的是,足智多谋的 Temani Afif 给出了 解释和解决方案

简单来说,问题是**浏览器仅在鼠标按下时激活的元素与鼠标抬起时激活的元素相同的情况下才会触发 click/press 事件**。

由于旋转动画,鼠标按下时的顶部标签并非鼠标抬起时的顶部标签,除非我快速或缓慢地操作以使动画循环。这就是为什么增加动画时间可以隐藏这些问题的原因。

解决方案是应用“static”位置来打破堆叠上下文,并使用像::before::after这样的伪元素,并使用更高的z-index来占据其位置。这样,鼠标抬起时,活动标签将始终位于顶部。

/* The active tag will be static and moved out of the window */ 
label:active {
  margin-left: 200%;
  position: static;
}

/* A pseudo-element of the label occupies all the space with a higher z-index */
label:active::before {
  content: "";
  position: absolute;
  top: 0;
  right: 0;
  left: 0;
  bottom: 0;
  z-index: 10;
}

以下是包含解决方案的代码,动画时间更快

查看 CodePen 上 Alvaro Montoro (@alvaromontoro) 编写的
使用 CSS 生成伪随机数的演示

CodePen 上。

进行此更改后,剩下的唯一工作是创建一个小的界面来绘制一个假的骰子以供点击,然后 CSS 蛇梯游戏 就完成了。

此技术有一些明显的缺点

  • **它需要用户输入:**必须单击一个标签才能触发“随机数生成”。
  • **它不具有良好的扩展性:**它在少量值的情况下效果很好,但在较大范围内使用则很麻烦。
  • **它不是真正的随机,而是伪随机:**计算机可以轻松检测到在每个时刻将生成哪个值。

但另一方面,它是 100% CSS(无需预处理器或其他外部帮助程序),并且对于人类用户来说,它看起来可以是 100% 随机的。

说到手……这种方法不仅可以用于随机数,还可以用于任何随机的东西。在本例中,我们使用它来“随机”选择石头剪刀布游戏中电脑的选择

查看 CodePen 上 Alvaro Montoro (@alvaromontoro) 编写的
CSS 石头剪刀布

CodePen 上。