在CSS中尝试Random()函数
摘要
一篇探索新的CSS random()函数的博客文章,该函数允许将属性设置为随机值以实现创意设计,包含演示和浏览器支持详情。
暂无内容
查看缓存全文
缓存时间: 2026/06/28 10:56
# 在 CSS 中使用 `random()` 函数进行实验
来源:https://polypane.app/blog/experimenting-with-random-in-css/
CSS 正在引入一个 `random()` 函数,它允许你为属性设置随机值,从而创造出有趣且富有创意的设计。你可以在 Polypane 29+ 中测试该功能,也可以在启用实验性功能后的 Chromium 148+ 中使用。Safari 26.2+ 也支持 `random()`,但并未完全支持本文中展示的所有演示。Firefox 目前暂不支持 `random()`。
> 使用 Polypane 29+、Safari 26.2 或启用了实验性功能的 Chromium 148+ 来尝试本文中的演示。如果你想查看实际运行效果,可以[获取 Polypane](https://dashboard.polypane.app/register/) 并免费试用。
## 我制作了一些演示
我花了几个星期构思如何使用 `random()` 来创造有趣的设计、图案和背景。
### 关于代码示例的说明
我们在下面的代码示例中直接使用了 `random()`,并没有为尚不支持该函数的浏览器提供降级方案。如果你想看到实际效果,可以[获取 Polypane](https://dashboard.polypane.app/register/) 并免费试用。
代码示例中的 CSS 是完全可编辑的,但只显示每个效果的相关 CSS(例如,下面的 bokeh 效果展示了定位,但没有展示 `border-radius`)。你可以编辑示例中的 CSS 来观察其工作原理。
在代码示例中,我们只展示了最相关的 CSS,但如果你复制代码,将会得到完整的 HTML 和 CSS。点击 "randomize" 按钮可以为每个属性生成新的随机值,并重新加载示例。
### 关于浏览器支持的说明
浏览器的实现仍在进行中,目前该功能放在实验性 Web 特性标志后面是有原因的。部分演示在 Safari 中无法正常工作。我们相当肯定下面的一些示例无法经受未来实现的变更,并且为了同时支持 Polypane 29(基于 Chromium 148)和更高版本的 Chromium(因为实现从 148、149 到 150 版本有所演变),我们不得不使用一些技巧。我们会持续更新本文,但如果你在将来阅读时发现某些演示不再工作,请告诉我们,我们会及时更新文章!
## Bokeh 效果
当我看到 `random()` 在 [Polypane 29 中可用](https://polypane.app/experimental-web-platform-features/?search=random)时,我立刻就想尝试一下。我最喜欢的“随机设计”类型之一是 *bokeh*,这是一种摄影效果,能形成失焦的光斑。我觉得用 `random()` 来创建 bokeh 效果会很有趣。于是我查阅了 MDN 上关于 [random() 的页面](https://developer.mozilla.org/zh-CN/docs/Web/CSS/Reference/Values/random) 并开始动手。
如果你观察 bokeh 效果,它本质上是一堆圆形,这些圆形在以下几个方面有所不同:
- 圆形的**位置**
- 圆形的**大小**
- 圆形的**颜色**
- 圆形的**失焦程度**
- 它们**变化的速度**(如果是动画 bokeh)
### 位置
圆形的定位最简单:将它们设置为 `position: absolute`,然后在 `left` 和 `top` 属性中使用 `random()`。这样就能将圆形放置在随机位置:
#### Bokeh:随机位置
`random()` 函数最简单的形式是接受一个最小值(0%)和一个最大值(100%),并返回两者之间的随机值。
> **注意:** 这里的最小值是 0,但必须包含 %,以确保两个值属于同一类型。如果只写 `random(0, 100%)`,则无效,因为最小值是 ``,最大值是 ``(类型不匹配)。
### 大小
bokeh 效果中的每个圆形大小不同,但它们仍然是圆形。因此,如果我们想用 `random()` 来设置大小,就需要确保宽度和高度是相同的值。我的想法是将随机值“存储”在一个自定义属性中(例如 `--size: random(50px, 20vmin)`),然后将该自定义属性同时用于宽度和高度。这样,宽度和高度就会始终相同。至少我是这么认为的。
#### Bokeh:随机椭圆
哎呀,宽度和高度不一样了!当你将自定义属性设置为随机值时,每次**使用**该自定义属性时都会生成一个新的随机值,而不是在声明时固定。为了解决这个问题,我们需要在 random 函数中添加一个自定义关键字 `element-scoped`。这会告诉浏览器为每个元素生成一个随机值。这样,每个元素会得到不同的随机值,但每个元素的宽度和高度是相同的:
#### Bokeh:使用 element-scoped 修复大小
> 注意:`element-scoped` 并非此特性的最终名称,但目前 Polypane 和 Safari 支持的是这个名称。它将被重命名为 `per-element`。这两个名称均尚未收录到 MDN 文档中,不过[已经有人提交了 PR](https://github.com/mdn/content/pull/44544) 来添加该内容。
### 重新审视位置
我最初想从位置开始讲,因为它最简单,但实际上位置存在一个问题:如果某个圆形从左侧 99% 开始,并且大小为 100px,那么它会溢出容器并导致滚动条。为了解决这个问题,我们可以使用 `calc()` 从位置中减去圆形的大小:
#### Bokeh:使用 element-scoped 修复大小
我们并不确切知道圆形的大小,但由于 `--size` 在每个属性中对于同一个元素是相同的,因此我们可以用它来确保圆形始终完全位于容器内。
### 圆形的颜色
对于圆形的颜色,我希望所有圆形的亮度和饱和度大致相同,但每个圆形具有不同的色相。因此,我在 `hsl()` 函数中对色相值使用了 `random()`。此外,当你观察 bokeh 图像时,有些圆形更明显,有些则不那么明显。所以我添加了一个随机的不透明度:
#### Bokeh:颜色与不透明度
HSL 中的 `hue` 只是一个数字,因此不需要单位。不透明度也是如此。
> 注意:为了更清晰地展示效果,我们在本示例及接下来的几个示例中增加了圆形的数量。
### 失焦程度
接下来,bokeh 图像中的一些圆形非常清晰,而另一些则比较模糊。在 CSS 中,我们可以使用 `filter: blur()` 函数来实现模糊效果。添加模糊后,某些颜色的鲜艳程度会有所损失,所以我添加了一个 `contrast()` 滤镜来使某些圆形更加鲜艳。最后,我添加了 `mix-blend-mode`,使用 "plus-lighter" 模式使圆形之间相互混合。这可以增加重叠区域的亮度,使其看起来更像真实的 bokeh:
#### Bokeh:模糊、对比度、混合
模糊效果有助于使圆形看起来更像真实的 bokeh 光斑,而对比度则使其中一些圆形看起来更鲜艳,另一些更暗淡,从而增强了效果。对于对比度,我们特意选择了一个范围,使得大多数随机值会**增加**对比度(100% 到 200% 范围),只有少数圆形的对比度值会降低对比度(50% 到 100% 范围)。
### 动画
最后,我想为 bokeh 圆形添加一些动画效果。当你通过相机观察 bokeh 并调整焦距时,一些圆形会变大且更模糊,而另一些则会变小且更清晰。最容易实现的方法就是使用简单的脉冲动画。我们只需要定义 50% 的关键帧。0% 和 100% 关键帧将是元素的默认状态(scale 为 1)。
```css
@keyframes pulse {
50% { scale: 1.2; }
}
```
我们可以对所有圆形重复使用同一个动画,因为我们可以为每个圆形设置随机的动画持续时间和动画延迟。这样,每个圆形会以不同的速率脉冲,并在不同的时间开始,从而使动画看起来更自然:
```css
.bokeh {
animation: pulse random(2s, 5s) ease-in-out infinite;
animation-delay: random(-5s, 0s);
}
```
我们将动画设置为无限循环,使用 ease-in-out 缓动函数来平滑每个脉冲的开始和结束。通过设置随机的**负**延迟,一些圆形会从动画的中间开始,这样在页面刷新时看起来更自然。如果我们只使用无延迟或正延迟,所有圆形都会从放大开始动画。
### 综合所有
因此,当我们把所有属性组合在一起时,就会得到:
#### Bokeh:最终完整演示
> **注意:** 对于像上面这样包含动画的演示,我们会显示一个 "show demo" 按钮来激活预览,原因有二:1)动画可能会分散注意力或令人厌烦;2)动画可能对性能有影响。
## 飘落的樱花花瓣
我家后院有一棵樱花树,春天风吹过时,花瓣会从树上飘落下来,缓缓落到地面。非常舒缓。我觉得用 `random()` 来重现这个效果会很有趣,我们先展示最终动画,让你了解我们要实现的目标:
#### 飘落的花瓣
> **注意:** 在 Safari 中,有时你无法在演示中看到任何花瓣。如果发生这种情况,请多次点击 randomize。我不知道为什么会发生这种情况,但似乎是 Safari 的一个 bug。
让我们看看要实现这个效果需要随机化哪些部分。
- 所有花瓣都需要从屏幕外的不同位置开始
- 它们应该大小略有不同,并且外观略有差异
- 它们都需要大致朝着相同的方向移动,仿佛被风吹动,但每片花瓣的飘落方式应该略有不同
- 它们应该以不同的速度飘落
- 它们在飘落时应随机旋转
### 起始位置
每片花瓣的起始位置应在当前视口上方,并随机分布在视口宽度方向,这样每片花瓣就会从不同的位置飘落。对于 `top` 值,我们可以使用固定值,使它们都从同一高度开始;对于水平起始值,我们将使用自定义属性,因为我们稍后在飘落动画中会再次用到它。我们将使用 `element-scoped`,这样每片花瓣都有不同的起始位置。我们不设置 `left`,而是设置 `translate`,因为我们希望动画让花瓣沿屏幕飘落,而动画 `translate` 比动画 `left` 性能更好:
```css
.petal {
position: absolute;
top: -100px;
--initial-position: random(element-scoped, -40vw, 100vw);
translate: var(--initial-position) 0;
}
```
我们本可以展示一个演示,但所有花瓣都从屏幕外开始且没有动画,所以什么也看不到。起始位置已搞定,接下来看大小和形状。
### 大小和花瓣样式
对于大小,这次我们使用 `aspect-ratio` 而不是显式的 `width`,这样可以保持简单:
```css
.petal {
width: random(30px, 50px);
aspect-ratio: 1/1;
}
```
对于花瓣本身,我找到了 Pram Samnak 设计的四种不同的花瓣,我很喜欢,并想随机选择其中一种。理论上,你可以使用 `random-item()` 来随机挑选这四种花瓣之一,但该函数尚未在任何浏览器中实现。如果它有效,代码看起来会像这样:
```css
.petal {
background-image: random-item(
url(petals/1.png),
url(petals/2.png),
url(petals/3.png),
url(petals/4.png)
);
}
```
我思考了一番如何绕过这个问题,最后意识到既然每片花瓣从随机位置开始,我就可以直接使用 `nth-child`。`nth-child()` 选择器的重复模式不会很明显,因为每片花瓣的起始位置是随机的。所有这些仍然发生在屏幕外,所以没有演示。让我们快速添加飘落动画,看看效果!
### 飘落动画
花瓣飘落时也会被风吹动,所以它们不会直线下落。为了实现这一点,我们可以对 `translate` 进行动画,使花瓣向下并向右侧移动。由于每片花瓣受到的风力不同,我们希望它们以不同的速度和不同的角度飘落。对于速度,我们可以改变动画的持续时间。对于角度,我们可以设置 `translate` 的终点值,该值使用我们已经有的 `--initial-position`,再加上一个随机量。
#### 飘落的花瓣:飘落动画
与 bokeh 示例类似,每片花瓣有不同的动画持续时间,并且我们设置一个负的延迟,这样一些花瓣从动画中间开始,使页面加载时看起来更自然。我们只需将 `to` 关键帧设置为屏幕底部的终点位置。当花瓣离开屏幕时,它会跳回顶部并再次开始飘落,从而实现连续效果。
### 旋转动画
为了实现花瓣在 3D 空间中旋转的效果,我们在它们飘落时随机缩放它们,仿佛它们正在靠近或远离摄像机,同时沿 X、Y 和 Z 轴旋转它们。我们使用两个独立的动画来实现这一点,这样两个动画可以以不同的随机持续时间和起始时间发生,为每片花瓣创造出独特的动画效果。对于旋转,我们使用带有 `perspective()` 的变换,以获得更好的 3D 视觉效果。我们还希望每片花瓣以不同的速度旋转,因此在动画持续时间中也使用了 `random()`。最后,我们将 transform-origin 设置为元素的 `top center`,这样花瓣会围绕该点旋转,而不是围绕花瓣的中心。
#### 飘落的花瓣:旋转
最后,我们添加了一张精美的背景图片作为背景。
## 一叠可以扔开的宝丽来照片
在第三个示例中,我想创建一叠“不整齐”的宝丽来照片,它们略微旋转并相互偏移,形成自然堆叠的外观。此外,我希望点击它们时能随机方向扔开。让我们看看如何为随机性添加交互!
### 构建堆叠
创建元素堆叠的最简单方法是使用 CSS Grid,并将所有元素放置在同一个网格单元格中:
```css
.container {
position: absolute;
margin: 0;
inset: 0px;
display: grid;
grid-template-columns: 1fr;
place-items: center;
.polaroid {
grid-area: 1/1/2/2;
}
}
```
然后我们可以为每张宝丽来照片设置样式。这次它们都有相同的尺寸,但我们将在 `transform` 中使用随机值来增加随机性。每张宝丽来的 HTML 结构如下:
```html
<div class="polaroid">
<img src="...">
<p>...</p>
</div>
```
由于网格布局,它们已经全部堆叠在相同的位置。我们可以在 `transform` 属性中使用 `random()` 来随机旋转和偏移每张卡片:
#### 宝丽来:随机堆叠
这里的 transform 负责整个效果:在 -15 到 15 度之间随机旋转,然后在两个方向上随机平移最多 10 像素。这创造了一叠漂亮的“凌乱”宝丽来照片。
### 添加交互
对于交互,我们需要在点击每张卡片时能够设置其状态,并且理想情况下还需要一种方法将卡片重置回原始位置。每张卡片都有一个复选框,我们将其叠加在卡片上方:
```html
<div class="polaroid">
<input type="checkbox" id="card1">
<img src="...">
<p>...</p>
</div>
```
CSS 确保复选框不可见且覆盖整张卡片:
```css
.polaroid {
input {
position: absolute;
inset: 0px;
opacity: 0;
cursor: pointer;
}
}
```
这样我们就可以使用每张卡片的 `:checked` 状态,但要取消选中,需要再次单独点击每张卡片。如果我们希望一次重置所有卡片,就需要使用平台。通过将所有复选框包裹在一个 `` 中,我们可以利用表单的原生 `reset` 行为,在点击重置按钮时一次性取消所有复选框。将重置按钮*放在*相同网格堆叠中的卡片*之前*,它会在最后一张宝丽来被移除时出现。
相似文章
什么是随机生成?
本文探讨了计算机中的伪随机数生成,重点聚焦于线性同余生成器(LCG)及其质量可视化。文章还提及了 Cloudflare 的熔岩灯等熵源,并作为基于属性的测试的前导内容。
CSS:不可避免的坏部分
一位非Web开发者的个人博客文章讨论了CSS中不可避免的坏部分,包括布局难题、浏览器默认设置和过度使用包装器,同时强调了可处理简单任务的子集。
CSSQuake
CSSQuake 是一个基于 CSS 的工具或演示,能够在网页上模拟地震般的抖动效果。
熵
一篇技术博文,探讨随机性、Linux熵以及构建一个名为morerandom的工具,该工具使用WASM插件来为系统熵池提供熵。
算法主题引擎
本文介绍了新的CSS `contrast-color()`函数,该函数允许开发人员自动选择黑色或白色文本,以实现与任何背景颜色的可访问对比度,解决网络上长期存在的低对比度问题,而无需依赖JavaScript。