介绍
我们将学习如何使用 Three.js 创建一个具有动态卡片动画和粒子效果的 3D 场景。项目包括:
- 卡片的动态进入与点击旋转动画
- 背景粒子效果
- 通过鼠标交互实现卡片旋转
HTML 和 CSS
HTML 文件是项目的基础结构,用于引入相关的依赖和定义渲染 3D 场景的容器:
- 页面结构
<div id="scene-container"></div>
是 3D 场景渲染的目标,所有的 Three.js 对象都会渲染在这里。- 使用 Import Map 来引入
three.js
和其插件,使代码模块化。 - 引入 GSAP 动画库,以实现动态效果。
以下是 HTML 文件的代码:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>3D Christmas Animation</title>
<link rel="stylesheet" href="style.css" />
<script src="https://cdnjs.cloudflare.com/ajax/libs/gsap/3.12.2/gsap.min.js"></script>
</head>
<body>
<div id="scene-container"></div>
<script type="importmap">
{
"imports": {
"three": "https://unpkg.com/[email protected]/build/three.module.js",
"three/addons/": "https://unpkg.com/[email protected]/examples/jsm/"
}
}
</script>
<script type="module" src="main.js"></script>
</body>
</html>
- 页面样式
- 清除浏览器的默认样式,确保各个浏览器渲染一致。
- 设置页面背景为深灰色,突出 3D 场景效果。
- 隐藏滚动条,避免超出页面边界时出现视觉干扰。
以下是 CSS 文件的代码:
* {
margin: 0;
padding: 0;
}
body {
overflow: hidden;
margin: 0;
font-family: "Arial", sans-serif;
background-color: #2e2e2e;
}
到此,我们可以看到一个灰色的背景
使用 JavaScript 创建 3D 动画
在 JavaScript 部分,我们将详细分步骤创建和管理 3D 场景,包括初始化场景、添加灯光、动态对象和用户交互功能。
1. 初始化场景
首先,创建一个 ChristmasAnimation
类,负责初始化场景和管理 3D 对象。
this.container
选择 HTML 中的#scene-container
,作为渲染目标。this.scene
创建一个 Three.js 场景对象,所有 3D 对象都会添加到这个场景中。this.cards
用于存储卡片对象,方便后续交互处理。setupScene
调用多个方法初始化摄像机、灯光、渲染器、控制器及其他对象。
代码:
class ChristmasAnimation {
constructor() {
this.container = document.querySelector("#scene-container");
this.scene = new THREE.Scene();
this.cards = [];
this.setupScene();
}
setupScene() {
this.createCamera(); // 创建相机
this.createLights(); // 创建灯光
this.createRenderer(); // 创建渲染器
this.createControls(); // 创建控制器
this.createCards(); // 创建卡片
this.createParticles(); // 创建粒子效果
this.renderer.setAnimationLoop(() => this.render()); // 渲染循环
}
}
2. 配置相机与灯光
相机用于捕捉场景,灯光用于照亮 3D 对象。
-
相机
- 使用 Three.js 的透视相机 (
PerspectiveCamera
),模拟人眼视角。 - 设置
camera.position
让相机位于场景的前方。
- 使用 Three.js 的透视相机 (
-
灯光
- 环境光 (
HemisphereLight
) 提供均匀照明。 - 平行光 (
DirectionalLight
) 模拟阳光效果,增强明暗对比。
- 环境光 (
代码:
createCamera() {
this.camera = new THREE.PerspectiveCamera(35, window.innerWidth / window.innerHeight, 0.1, 1000);
this.camera.position.set(0, 0, 15);
}
createLights() {
const ambientLight = new THREE.HemisphereLight(0xddeeff, 0x202020, 5);
const mainLight = new THREE.DirectionalLight(0xffffff, 5);
mainLight.position.set(10, 10, 10);
this.scene.add(ambientLight, mainLight);
}
3. 创建渲染器和控制器
渲染器将 3D 场景绘制到浏览器,控制器允许用户交互。
-
渲染器
- 创建 WebGL 渲染器并启用抗锯齿。
- 设置画布大小为窗口尺寸。
-
控制器
- 使用轨道控制器 (
OrbitControls
) 实现鼠标拖拽、旋转和缩放。 - 启用阻尼效果(
enableDamping
),让交互更自然。
- 使用轨道控制器 (
代码:
createRenderer() {
this.renderer = new THREE.WebGLRenderer({ antialias: true, alpha: true });
this.renderer.setSize(window.innerWidth, window.innerHeight);
this.container.appendChild(this.renderer.domElement);
}
createControls() {
this.controls = new OrbitControls(this.camera, this.renderer.domElement);
this.controls.enableDamping = true;
}
4.添加动态卡片
在 3D 场景中添加卡片并实现动态动画,可以拆分为两个逻辑:卡片的创建和卡片的动画。
创建卡片
-
- 使用平面几何体 (
PlaneGeometry
) 创建卡片形状,每张卡片大小为宽 2,高 3。
- 使用平面几何体 (
- 使用
TextureLoader
加载图片,作为卡片的纹理材质。 - 将卡片添加到场景中,初始位置设置在场景的下方。
- 图片来源为
https://picsum.photos
,这是一个提供随机图片生成的服务,非常适合开发和测试用途。其 URL 格式为https://picsum.photos/{宽度}/{高度}
,可以根据需求指定图片尺寸。
代码:
createCards() {
const cardGeometry = new THREE.PlaneGeometry(2, 3); // 定义卡片几何体
const textureLoader = new THREE.TextureLoader(); // 创建纹理加载器
const cardTextures = [
"https://picsum.photos/600/768",
"https://picsum.photos/600/769",
"https://picsum.photos/600/770"
];
cardTextures.forEach((url, index) => {
textureLoader.load(url, (texture) => {
const material = new THREE.MeshStandardMaterial({ map: texture }); // 使用纹理创建材质
const card = new THREE.Mesh(cardGeometry, material); // 创建卡片网格
card.position.set(index * 4 - 4, -10, 0); // 将卡片放置在场景下方
this.scene.add(card); // 添加卡片到场景
// 卡片动画逻辑(见下文)
this.animateCard(card, index);
});
});
}
卡片动画
解释:
- 使用 GSAP 动画库让卡片从场景下方上升到目标位置。
- 为每张卡片设置不同的延迟时间,形成依次上升的效果。
gsap.to(card.position, {
y: 0, // 最终位置的 y 值
duration: 1.5, // 动画持续时间
delay: index * 0.2, // 动画延迟
ease: "elastic.out(1, 0.5)", // 弹性动画效果
});
5. 添加粒子效果
粒子是随机分布的小点,提供背景装饰。
解释:
- 使用
BufferGeometry
创建粒子位置数据。 - 使用
PointsMaterial
定义粒子的颜色和大小。
代码:
createParticles() {
const particleGeometry = new THREE.BufferGeometry();
const positions = new Float32Array(1000 * 3);
for (let i = 0; i < positions.length; i += 3) {
positions[i] = (Math.random() - 0.5) * 20;
positions[i + 1] = Math.random() * 20 - 5;
positions[i + 2] = (Math.random() - 0.5) * 20;
}
particleGeometry.setAttribute("position", new THREE.BufferAttribute(positions, 3));
const particleMaterial = new THREE.PointsMaterial({ color: 0xffffff, size: 0.05 });
const particles = new THREE.Points(particleGeometry, particleMaterial);
this.scene.add(particles);
}
6. 处理交互
交互功能让用户可以点击卡片并触发旋转动画。
解释:
- 使用
Raycaster
实现光线投射,检测用户点击的对象。 - 如果用户点击了卡片,触发 GSAP 动画实现旋转效果。
代码:
onCardClick(event) {
const mouse = new THREE.Vector2(
(event.clientX / window.innerWidth) * 2 - 1,
-(event.clientY / window.innerHeight) * 2 + 1
);
const raycaster = new THREE.Raycaster();
raycaster.setFromCamera(mouse, this.camera);
const intersects = raycaster.intersectObjects(this.cards);
if (intersects.length > 0) {
gsap.to(intersects[0].object.rotation, { y: "+=3.1415", duration: 1 });
}
}
代码
github
https://github.com/calmound/threejs-demo/tree/main/card
gitee
https://gitee.com/calmound/threejs-demo/tree/main/card
学习Three.js
https://threejs3d.com/