学完 SpriteText 之后,我们来做一个实战:数字雨
也就是黑客帝国里的这种效果:

这种明显要用 Sprite 来做,而文字则是要用 canvas 画,作为 CanvasTexture 设置到 Sprite。
这种需求一般都是直接用 three-spritetext 这个库来做,上节刚学过。
我们来写一下:
npx create-vite number-rain

进入项目,安装依赖:
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


可以看到,数字显示出来了。
但我们相机位置不大对。
我们把相机移到中间位置,也就是 width / 2, height / 2, 0 的位置。

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,注释掉即可。

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

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

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

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

接下来让它们动起来:

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

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


这样,黑客帝国的那种数字雨的效果就完成了。
案例代码上传了小册仓库。
总结
这节我们练习了下 SpriteText,用它实现了一个数字雨的效果。
SpriteText 用来绘制文字,不需要自己用 canvas + Sprite 来绘制。
然后我们计算了下一共显示多少列、每列显示多少个数字,创建并设置每个数字的位置,之后每一帧都改变 y 就好了。
以后需要绘制文字的需求都可以直接用 three-spritetext