最近在短视频平台,你很可能刷到过拉斯维加斯的新地标「Sphere」巨型球体。

其场馆内部的裸眼3D视觉效果极具冲击力,其中一个动态效果尤其让我印象深刻。

我的第一反应是:这个效果用 CSS 是不是也能模拟出来?毕竟,还有什么是 CSS 不能尝试实现的吗?
本文就将带领大家,一步步使用纯 CSS 技术,大致还原这个震撼的视觉效果。
拆解动画效果
本质上,这个动画是一个动态变化的 3D 立方体。立方体的每个面上,布满了颜色和字符都在随机、快速跳变的“数字雨”或“字符流”。
也就是说,我们需要完成两个核心部分:
- 构建一个 3D 立方体。
- 实现一个字符和颜色都随机变化的平面动画。
最后将两者结合,并调整 3D 空间的景深和视角,就能逼近目标效果。
好,让我们分步实现。
实现一个 3D 立方体
对于有经验的前端开发者来说,构建一个基础的 3D 立方体是相对简单的任务。在很多探讨 CSS 3D 的文章中都有涉及。
一个最常见的 3D 图形就是立方体。如果先不考虑上下底面,一个只有四个侧面的结构大致像这样:

那么,如何用 CSS 3D 快速构建它呢?
首先,准备 HTML 结构:
<div class="perspective">
<div class="container">
<div class="img">3</div>
<div class="img">D</div>
<div class="img">视</div>
<div class="img">图</div>
</div>
</div>
四个面就是四个 .img 元素。接着,需要为它们的父级容器设置 3D 相关属性:
.perspective {
perspective: 3000px;
}
.container {
width: 400px;
height: 400px;
transform-style: preserve-3d;
}
简单解释一下:
perspective 属性为后代元素设置透视点,放在最外层的 .perspective 上即可。
transform-style: preserve-3d 用于建立 3D 渲染空间。因为最终是 .img 元素需要进行 3D 变换,所以这个属性设置在它们的直接父容器 .container 上。
接下来是关键:如何设置 4 个 .img 的变换,让它们围成一个立方体?核心技巧是:先旋转,再位移。
这里有一张俯视图来帮助理解:

假设每个 .img 是一个 400px * 400px 的正方形。要拼成一个立方体,就需要将四个面分别绕 Y 轴旋转 90°、180°、270°、360°(即0°),然后再沿着 Z 轴正向平移 200px(即立方体边长的一半)。
请注意,变换的顺序至关重要,必须是先 rotateY() 再 translateZ()。
对应的 CSS 可以这样写(这里使用了 SASS 循环):
.img {
position: absolute;
top: 0;
left: 0;
width: 400px;
height: 400px;
}
@for $i from 1 through $imgCount {
.img:nth-child(#{$i}) {
transform: rotateY(($i * 90deg)) translateZ(200px);
}
}
效果如下:

此时立方体可能看起来太大。我们可以通过调整中间层 .container 的 translateZ 值来改变它在视觉上的大小。
.container {
transform: translateZ(-3000px);
}
这样,就能得到一个大小合适的立方体效果:

完整的代码,你可以戳这里:CodePen Demo -- 3D Cube (Demo 链接在下方参考资料)
当然,为了模拟目标效果,我们需要一个五面体(包含前、后、左、右、上五个面)。基于上面的原理,我们调整一下框架结构:
<div class="perspective">
<div class="container">
<div class="g-panel"></div>
<div class="g-panel"></div>
<div class="g-panel"></div>
<div class="g-panel"></div>
<div class="g-panel"></div>
</div>
</div>
我们只需微调每个面的尺寸和 translateZ 的距离,就能得到我们所需的五面立方体结构。
示意效果如下:

实现文字动画效果
好,立方体部分先放一放。
接下来,我们集中精力实现这个单面的动态效果:

如果每个字符都用一个独立的 DIV 元素来实现,虽然简单,但会导致元素数量爆炸,在施加动画时极易引起页面卡顿。因此,我们需要寻找更高效的方案。
这里,我们可以巧妙运用多重线性渐变配合 background-clip: text 属性来实现。
首先,我们用等宽字体创建一列文字:
<div>ABCDEFGHIJKLMN</div>
div {
font-family: monospace;
text-align: center;
font-size: 25px;
width: 25px;
line-height: 25px;
color: #fff;
}
效果大致如下:

接着,我们利用线性渐变,为每个字符所在的高度区域(25px * 25px)随机设置不同的颜色。这可以通过 SASS 函数动态生成:
@function randomLinear($count) {
$value: '';
@for $i from 0 through ($count - 1) {
$value: $value + randomColor() + string.unquote(" 0 #{$i * 25}px,");
}
@return linear-gradient(string.unquote(#{$value}) randomColor() 0 100%);
}
@function randomColor() {
@return rgb(randomNum(255), randomNum(255), randomNum(255));
}
div {
// ...
background: randomLinear(14);
}
randomLinear(14) 会生成14层线性渐变,每层对应一个文字高度,颜色随机。编译后的CSS可能像这样:
div {
// ...
background: linear-gradient(#feea96 0 25px, #edde42 0 50px, #e2344a 0 75px, #cdab7e 0 100px, #e16c8b 0 125px, #dcdc7d 0 150px, #dcb42a 0 175px, #d6a587 0 200px, #984f71 0 225px, #221e34 0 250px, #5e9a69 0 275px, #a955e4 0 300px, #4e908f 0 325px, #8d177e 0 350px);
}
此时,每个高度区间有了不同的颜色背景:

现在,我们只需加上 background-clip: text 并将文字颜色设为透明 color: transparent,就能实现“一列文字,每个字颜色都不同”的效果:
div {
// ...
background: randomLinear(14);
background-clip: text;
color: transparent;
}
效果如下:

文字颜色能随机了,那文字内容本身呢?同样可以随机。我们再编写一个 SASS 函数来生成随机字符,并通过伪元素的 content 属性来设置。
于是,完整的单列代码可能如下:
<div></div>
$str: 'QWERTYUIOPASDFGHJKLZXCVBNMabcdefghigklmnopqrstuvwxyz123456789';
$length: str-length($str);
@function randomLinear($count) {
$value: '';
@for $i from 0 through ($count - 1) {
$value: $value + randomColor() + string.unquote(" 0 #{$i * 25}px,");
}
@return linear-gradient(string.unquote(#{$value}) randomColor() 0 100%);
}
@function randomColor() {
@return rgb(randomNum(255), randomNum(255), randomNum(255));
}
@function randomChar() {
$r: random($length);
@return str-slice($str, $r, $r);
}
@function randomChars($number) {
$value: '';
@if $number > 0 {
@for $i from 1 through $number {
$value: $value + randomChar();
}
}
@return $value;
}
div {
position: relative;
width: 25px;
height: 350px;
&::before {
content: randomChars(14);
position: absolute;
font-family: monospace;
background: randomLinear(14);
background-clip: text;
color: transparent;
text-align: center;
font-size: 25px;
width: 25px;
line-height: 25px;
}
}
这样,每次刷新,div 内的文字都是从预设字符集中随机取出的:

接下来是实现文字的随机跳变。思路是:在样式初始化时,就预先生成好几组不同的随机文字内容,存入 CSS 自定义属性(变量)中,然后通过 CSS 动画快速切换这些内容。
div {
&::before {
content: randomChars(14);
--content1: "#{randomChars(14)}";
--content2: "#{randomChars(14)}";
--content3: "#{randomChars(14)}";
--content4: "#{randomChars(14)}";
animation: contentChange 1s infinite;
}
}
@keyframes contentChange {
20% {
content: var(--content1);
}
40% {
content: var(--content2);
}
60% {
content: var(--content3);
}
80% {
content: var(--content4);
}
}
我们预先生成了5组内容(初始1组+4组变量),并在动画中循环切换,这样就得到了文字内容的随机跳变:

还差颜色的随机跳变。这个可以利用 filter: hue-rotate() 滤镜,结合 steps() 步进动画轻松实现。
div {
animation: colorChange 1s steps(12) infinite;
}
@keyframes colorChange {
100% {
filter: hue-rotate(360deg);
}
}
hue-rotate(360deg) 会让颜色色调循环一周,steps(12) 则让这个变化在1秒内分12次跳跃完成,从而实现了颜色的快速跳变效果:

当然,我们的目标不是一个单列,而是一个铺满整个面的动画。我们只需要基于上面的逻辑,使用 SASS 循环生成多列(比如32列),并将它们横向排列即可。
基于以上所有铺垫,我们最终实现单个动画平面的核心代码如下:
<div class="g-container">
<div></div>
// ... 一共32个子 div
<div></div>
</div>
@use "sass:string";
$str: 'QWERTYUIOPASDFGHJKLZXCVBNMabcdefghigklmnopqrstuvwxyz123456789';
$length: str-length($str);
$size: 25;
$count: 41;
@function randomNum($max, $min: 0, $u: 1) {
@return ($min + random($max)) * $u;
}
@function randomLinear($count) {
$value: '';
@for $i from 0 through ($count - 1) {
$value: $value + randomColor() + string.unquote(" 0 #{$i * 25}px,");
}
@return linear-gradient(string.unquote(#{$value}) randomColor() 0 100%);
}
@function randomColor() {
@return rgb(randomNum(255), randomNum(255), randomNum(255));
}
@function randomChar() {
$r: random($length);
@return str-slice($str, $r, $r);
}
@function randomChars($number) {
$value: '';
@if $number > 0 {
@for $i from 1 through $number {
$value: $value + randomChar();
}
}
@return $value;
}
body,
html {
width: 100%;
height: 100%;
background: #000;
font-family: monospace;
}
.g-container {
position: relative;
width: 800px;
height: 800px;
display: flex;
animation: colorChange 1s steps(12) infinite;
div {
position: relative;
width: #{$size}px;
height: 800px;
flex-shrink: 0;
&::before {
position: absolute;
inset: 0;
text-align: center;
font-size: #{$size}px;
width: #{$size}px;
text-align: center;
line-height: #{$size}px;
color: transparent;
}
}
@for $i from 1 to $count {
div:nth-child(#{$i}) {
&::before {
content: randomChars(32);
--content1: "#{randomChars(32)}";
--content2: "#{randomChars(32)}";
--content3: "#{randomChars(32)}";
--content4: "#{randomChars(32)}";
animation: contentChange 1s infinite;
background: randomLinear(32);
background-clip: text;
}
}
}
}
@keyframes colorChange {
100% {
filter: hue-rotate(360deg);
}
}
@keyframes contentChange {
20% {
content: var(--content1);
}
40% {
content: var(--content2);
}
60% {
content: var(--content3);
}
80% {
content: var(--content4);
}
}
这样,我们就成功地实现了一个完整的平面,其上的文字和颜色都在随机、快速地进行跳变动画:

单个平面效果的完整代码,你可以在这里查看:CodePen Demo -- Single Panel Random Text (Demo 链接在下方参考资料)
实现立体效果
有了上面的立方体和单面动画效果,组合成立体效果就水到渠成了。
改造我们之前的立方体结构,大致如下(这里使用 PUG 模板引擎简化书写):
.perspective
.container
.g-panel
-for(var i=0; i<32; i++)
div
.g-panel
-for(var i=0; i<32; i++)
div
.g-panel
-for(var i=0; i<32; i++)
div
.g-panel
-for(var i=0; i<32; i++)
div
.g-panel
-for(var i=0; i<32; i++)
div
编译后的 HTML 结构就是:一个立方体容器内,有5个 .g-panel(对应五个面),每个 .g-panel 内包含32个 div,这些 div 将承载我们上面实现的那个单列动画效果。
这样,每个 .g-panel 面都应用了绚丽的随机文字动画,组合起来就得到了一个立体的、五面都在动态变化的 3D 立方体动画:

接下来,我们只需要调整 perspective(透视距离)和 .container 的 translateZ(Z轴位移),将观察视角“放置”到这个立方体的内部,就能得到一种被动态字符包围的沉浸感:

最后,为了模拟文章开头拉斯维加斯球体那种“天花板坠落”的震撼效果,我们可以为顶部的那个平面添加一个向下运动的动画。至此,我们就使用纯 CSS,大致模拟出了整个复杂的视觉效果:

由于 GIF 录制问题,实际效果会比 GIF 展示的更为流畅和震撼。
使用纯 CSS 实现的完整代码及效果,你可以点击这里查看:CodePen Demo -- Las Vegas Sphere Cube Random Text (Demo 链接在下方参考资料)
最后
本文通过拆解一个复杂的视觉场景,逐步展示了如何运用 CSS 3D、CSS 动画、SASS 函数式编程以及 background-clip 等特性,将想法变为现实。这再次证明了现代 CSS 在创造丰富视觉体验方面的强大潜力。希望这个实践过程对你有所启发,如果你对这类 前端动效 感兴趣,欢迎在 云栈社区 与其他开发者继续交流探讨。
参考资料
[1] CodePen Demo -- 3D Cube: https://codepen.io/Chokcoco/pen/OJaLLpX
[2] CodePen Demo -- Single Panel Random Text: https://codepen.io/Chokcoco/pen/jOXJqVv?editors=1100
[3] CodePen Demo -- Las Vegas Sphere Cube Random Text: https://codepen.io/Chokcoco/pen/LYMMpPm?editors=1100
[4] Github -- iCSS: https://github.com/chokcoco/iCSS