CSS 允许您在 Web 上创建动态布局和界面,但作为一种语言,它是静态的:一旦设置了值,就无法更改。随机性的概念不在考虑范围内。在运行时生成随机数是 JavaScript 的领域,而不是 CSS。或者是吗?如果我们考虑一点用户交互,我们实际上可以在 CSS 中生成一定程度的随机性。让我们来看看!
来自其他语言的随机化
正如 Robin Rendle 在 CSS-Tricks 上的一篇文章中解释的那样,有一些方法可以使用 CSS 变量来获得一些“动态随机化”。但这些解决方案并非 100% CSS,因为它们需要 JavaScript 使用新随机值更新 CSS 变量。
我们可以使用 Sass 或 Less 等预处理器来生成随机值,但是一旦 CSS 代码编译并导出,这些值就会固定,随机性就会丢失。正如 Jake Albaugh 解释的那样
sass 中的随机就像随机选择故事中主角的名字一样。只有在编写时才是随机的。它不会改变。
— jake albaugh (@jake_albaugh) 2016 年 12 月 29 日
为什么我在乎 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 上。
精彩的文章!!
首先想到的是
.dice[data-random]::before {content:attr(data-random);}
需要另一种语言(JS、PHP 等)在 HTML 中填充
data-random
属性,但我认为它更可靠。使用另一种语言肯定更可靠、更有效。这更像是一种有趣的“不要在生产环境中使用”的方法。
喜欢这篇文章!我一直深入研究 Javascript 和随机化(数组元素的随机化),并且一直在质疑纯 CSS 解决方案。这段旅程很有趣,在构建有趣的演示的同时,也让我头撞键盘。感谢分享。
PS 石头剪刀布……CTA 事件导致我的移动浏览器重新加载并重新滚动到底部以查看结果(结果是平局……唉,哈哈)。只是一个观察结果。
谢谢!这也总结了我使用 CSS 和随机化的经验 :)
关于向上滚动,似乎是 CodePen 在嵌入到其他网站时出现的问题。
很棒的文章!这才是真正的 CSS 技巧! :)
我想尝试一下,所以我制作了一个动画骰子!
连续点击骰子几次后,我注意到它实际上一直在下降——从 6 到 0。
但如果它是随机的,我的意思是它也应该随机显示 5 或 6。对吧?
但它没有
非常棒!我之前尝试过做类似的事情(https://codepen.io/IAmAdamTaylor/pen/wqVaeP),但我无法解决鼠标按下/鼠标抬起的问题——顺便说一句,你的解决方案非常棒。
旁注:应该包含解决方案和更快动画时间的 CodePen 与上面的那个相同。