Skip to content

53. 实战:数字雨

Published:

学完 SpriteText 之后,我们来做一个实战:数字雨

也就是黑客帝国里的这种效果:

image.png

这种明显要用 Sprite 来做,而文字则是要用 canvas 画,作为 CanvasTexture 设置到 Sprite。

这种需求一般都是直接用 three-spritetext 这个库来做,上节刚学过。

我们来写一下:

npx create-vite number-rain

image.png

进入项目,安装依赖:

npm install
npm install --save three
npm install --save-dev @types/three

改下 src/main.js

import './style.css';
import * as THREE from 'three';
import {
    OrbitControls
} from 'three/addons/controls/OrbitControls.js';
import mesh from './mesh.js';

const scene = new THREE.Scene();

scene.add(mesh);

const ambientLight = new THREE.AmbientLight();
scene.add(ambientLight);

const width = window.innerWidth;
const height = window.innerHeight;

const helper = new THREE.AxesHelper(500);
scene.add(helper);

const camera = new THREE.PerspectiveCamera(60, width / height, 300, 10000);
camera.position.set(500, 500, 800);

const renderer = new THREE.WebGLRenderer();
renderer.setSize(width, height)

const controls = new OrbitControls(camera, renderer.domElement);

function render() {
    renderer.render(scene, camera);
    requestAnimationFrame(render);
}

render();

document.body.append(renderer.domElement);

基础代码和之前一样

创建 Scene、Light、Camera、Renderer。

改下 style.css

body {
  margin: 0;
}

然后在 mesh.js 写一下数字雨的代码

安装 three-spritetext

npm install --save three-spritetext

创建 mesh.js

import * as THREE from "three";
import SpriteText from "three-spritetext";

const width = window.innerWidth;
const height = window.innerHeight;

const columnWidth = 50;
const columnNum = Math.floor(width / columnWidth);

const fontSize = 30;
const lineHeight = fontSize * 1.3;

const textNumOfColumn = Math.ceil(height * 2 / lineHeight);

const group = new THREE.Group();
const columns = [];

for (let i = 0; i < columnNum; i++) {
  const column = [];

  for (let j = 0; j < textNumOfColumn; j++) {
    const text = Math.floor(Math.random() * 10) + '';

    const spriteText = new SpriteText(text, 20, 'green');

    const x = i * columnWidth;
    const y = j * lineHeight;
    spriteText.position.set(x, y, 0);

    group.add(spriteText);
    column.push(spriteText);
  }
  columns.push(column);
}

export default group;

整体的宽高就是窗口的宽高 window.innerWidth、window.innerHeight

我们设定每列的宽度是 50,那一共就是 width / columnWidth 列

文字大小是 30,行高 30 * 1.3,那每列就是 height / lineHeight 个文字,我们多显示一些 * 2

然后双重循环用 SpriteText 创建随机的数字,并且设置 x、y 就可以了。

用 i * columnWidth 计算列的偏移,j * lineHeight 计算在行中的位置。

跑一下:

npm run dev

image.png

2025-05-24 02.00.35.gif

可以看到,数字显示出来了。

但我们相机位置不大对。

我们把相机移到中间位置,也就是 width / 2, height / 2, 0 的位置。

image.png

camera.position.set(width / 2 , height / 2, 500);
camera.lookAt(width / 2 , height / 2, 0);

相机在 z 为 500 的位置望向 z 为 0 的位置。

这里要把 OrbitControls 注释掉,因为它默认会把 lookAt 重置到 0,0,0,除非设置它的 target 也为那个位置。

这里不需要 OrbitControls,注释掉即可。

image.png

现在,相机位置就对了。

现在的数字太整齐了,其实不需要那么整齐,我们来做一点随机的偏移:

image.png

const y = j * lineHeight + Math.random() * 60;

image.png

这样就好多了。

然后我们给它加个描边,这样有发光的感觉。

image.png

spriteText.strokeWidth = 1;
spriteText.strokeColor = 'lightgreen';

image.png

接下来让它们动起来:

image.png

让它们每帧随机下落一段距离, y 小于 0 就回到最上面,也就是 y 设置为 height

看下效果:

2025-05-24 02.16.53.gif

最后,我们设置下随机的透明度

image.png

2025-05-24 02.19.46.gif

这样,黑客帝国的那种数字雨的效果就完成了。

案例代码上传了小册仓库

总结

这节我们练习了下 SpriteText,用它实现了一个数字雨的效果。

SpriteText 用来绘制文字,不需要自己用 canvas + Sprite 来绘制。

然后我们计算了下一共显示多少列、每列显示多少个数字,创建并设置每个数字的位置,之后每一帧都改变 y 就好了。

以后需要绘制文字的需求都可以直接用 three-spritetext

评论