引言
在现代网络应用中,炫酷的视觉效果可以极大地提升用户体验和界面的吸引力。在这篇文章中,我们将探讨如何使用 Three.js 库创建一个具有动态烟花效果的三维文字展示场景。我们的目标是创建一个具有渐变颜色效果、动感十足的文字展示效果,同时配合生动的烟花爆炸动画,为用户呈现一个令人惊叹的视觉体验。
值此中秋佳节来临之际,提前预祝火山引擎开发者社区的小伙伴们儿,节日快乐,满满收获。
场景设置与初始化
首先,我们需要为我们的场景设置基本的 Three.js 环境。我们创建一个 THREE.Scene
实例,并设置背景颜色为深蓝色,以模拟夜空的效果。
let scene, camera, renderer, rockets = [], fireworks = [];
function init() {
scene = new THREE.Scene();
scene.background = new THREE.Color(0x002244); // 深蓝色背景
}
然后,我们设置相机 THREE.PerspectiveCamera
,这是一个透视相机,可以给用户提供一个真实的三维感。接着,我们初始化渲染器 THREE.WebGLRenderer
并将其添加到网页中:
camera = new THREE.PerspectiveCamera(75, window.innerWidth / window.innerHeight, 0.1, 1000);
camera.position.z = 50;
renderer = new THREE.WebGLRenderer({ antialias: true });
renderer.setSize(window.innerWidth, window.innerHeight);
document.body.appendChild(renderer.domElement);
添加光源
为了增强场景的立体感,我们添加了环境光和点光源。环境光提供均匀的照明,而点光源模拟了场景中的实际光源。
const ambientLight = new THREE.AmbientLight(0xffffff, 0.8);
scene.add(ambientLight);
const pointLight = new THREE.PointLight(0xffffff, 1.5, 100);
pointLight.position.set(10, 10, 10);
scene.add(pointLight);
加载字体并创建三维文字
在我们的场景中,我们需要加载自定义字体并用它创建动态的文字对象。我们使用 FontLoader
从本地 JSON 文件加载字体,并利用 TextGeometry
创建三维文字几何体。
const loader = new FontLoader();
loader.load('./json/STKaiti_Regular.json', function(font) {
let xOffset = -20; // 起始位置
const textData = [
{ text: '中秋快乐', color1: 0xff0000, color2: 0xffff00 },
{ text: '火山引擎', color1: 0x00ff00, color2: 0x0000ff },
{ text: '开发者社区', color1: 0x00ffff, color2: 0xff00ff }
];
textData.forEach(data => {
createRocket(font, data.text, data.color1, data.color2, xOffset);
xOffset += 15; // 每段文字之间的间距
});
});
创建渐变色字体
为字体添加渐变色可以使其更加引人注目。我们使用 THREE.ShaderMaterial
来实现这一效果。我们定义了顶点着色器和片元着色器,利用 UV 坐标在字体上实现颜色渐变。
function createRocket(font, text, color1, color2, xOffset) {
const textGeometry = new TextGeometry(text, {
font: font,
size: 5,
height: 2,
curveSegments: 12,
bevelEnabled: true,
bevelThickness: 0.5,
bevelSize: 0.3,
bevelSegments: 5
});
const material = new THREE.ShaderMaterial({
vertexShader: `
varying vec2 vUv;
void main() {
vUv = uv;
gl_Position = projectionMatrix * modelViewMatrix * vec4(position, 1.0);
}
`,
fragmentShader: `
uniform vec3 color1;
uniform vec3 color2;
varying vec2 vUv;
void main() {
vec3 color = mix(color1, color2, vUv.y);
gl_FragColor = vec4(color, 1.0);
}
`,
uniforms: {
color1: { value: new THREE.Color(color1) },
color2: { value: new THREE.Color(color2) }
},
side: THREE.FrontSide,
transparent: true
});
const rocket = new THREE.Mesh(textGeometry, material);
rocket.position.set(xOffset, -20, 0);
scene.add(rocket);
rockets.push({
mesh: rocket,
velocityY: 0.3 + Math.random() * 0.2,
targetHeight: 15 + Math.random() * 15,
exploded: false,
delay: Math.random() * 2
});
}
创建烟花效果
在文字升空并达到目标高度后,我们将触发烟花效果。我们利用 THREE.BufferGeometry
和 THREE.PointsMaterial
创建粒子系统,模拟烟花的爆炸效果。粒子系统通过随机位置和速度模拟烟花的飞散效果。
function createFirework(x, y) {
const particleCount = 200;
const particles = new THREE.BufferGeometry();
const positions = new Float32Array(particleCount * 3);
const velocities = new Float32Array(particleCount * 3);
const colors = new Float32Array(particleCount * 3);
for (let i = 0; i < particleCount; i++) {
const theta = Math.random() * 2 * Math.PI;
const phi = Math.acos((Math.random() * 2) - 1);
const distance = Math.random() * 20;
positions[i * 3] = distance * Math.sin(phi) * Math.cos(theta);
positions[i * 3 + 1] = distance * Math.sin(phi) * Math.sin(theta);
positions[i * 3 + 2] = distance * Math.cos(phi);
velocities[i * 3] = positions[i * 3] * 0.1;
velocities[i * 3 + 1] = positions[i * 3 + 1] * 0.1;
velocities[i * 3 + 2] = positions[i * 3 + 2] * 0.1;
colors[i * 3] = Math.random();
colors[i * 3 + 1] = Math.random();
colors[i * 3 + 2] = Math.random();
}
particles.setAttribute('position', new THREE.BufferAttribute(positions, 3));
particles.setAttribute('velocity', new THREE.BufferAttribute(velocities, 3));
particles.setAttribute('color', new THREE.BufferAttribute(colors, 3));
const material = new THREE.PointsMaterial({
size: 1.0,
vertexColors: true,
transparent: true,
opacity: 1
});
const particleSystem = new THREE.Points(particles, material);
particleSystem.position.set(x, y, Math.random() * 10 - 5);
scene.add(particleSystem);
fireworks.push(particleSystem);
}
动画循环与更新
在动画循环中,我们处理文字的上升动画和烟花效果的更新。文字在上升到目标高度后触发烟花效果,并在动画中逐渐消失。烟花的粒子在每一帧中更新位置,并模拟重力和空气阻力的效果。
function animate() {
requestAnimationFrame(animate);
rockets.forEach((rocket, index) => {
if (!rocket.exploded) {
if (performance.now() / 1000 > rocket.delay) {
rocket.mesh.position.y += rocket.velocityY;
rocket.mesh.rotation.y += 0.01;
rocket.mesh.scale.set(1.05, 1.05, 1.05);
if (rocket.mesh.position.y >= rocket.targetHeight) {
createFirework(rocket.mesh.position.x, rocket.mesh.position.y);
scene.remove(rocket.mesh);
rocket.exploded = true;
}
}
}
});
fireworks.forEach(firework => {
const positions = firework.geometry.attributes.position.array;
const velocities = firework.geometry.attributes.velocity.array;
for (let i = 0; i < positions.length; i += 3) {
positions[i] += velocities[i];
positions[i + 1] += velocities[i + 1];
positions[i + 2] += velocities[i + 2];
velocities[i + 1] -= 0.005;
velocities[i] *= 0.98;
velocities[i + 1] *= 0.98;
velocities[i + 2] *= 0.98;
}
firework.geometry.attributes.position.needsUpdate = true;
firework.material.opacity -= 0.01;
if (firework.material.opacity <= 0) {
scene.remove(firework);
fireworks = fireworks.filter(fw => fw !== firework);
}
});
renderer.render(scene, camera);
}
窗口大小调整
为了确保场景在窗口大小调整时能够正常显示,我们需要更新相机的纵横比和渲染器的大小。
window.addEventListener('resize', onWindowResize);
function onWindowResize() {
camera.aspect = window.innerWidth / window.innerHeight;
camera.updateProjectionMatrix();
renderer.setSize(window.innerWidth, window.innerHeight);
}
总结
通过上述代码,我们创建了一个炫酷的三维文字展示场景,其中包含动态的渐变色字体和生动的烟花效果。这个项目不仅展示了 Three.js 在创建复杂视觉效果方面的强大功能,也展示了如何利用自定义着色器和粒子系统来实现细致的动画效果。希望这篇文章能为你提供灵感,帮助你在自己的项目中创建令人惊叹的视觉效果。