Bootstrap

使用 Three.js 创建动态卡片动画

介绍

我们将学习如何使用 Three.js 创建一个具有动态卡片动画和粒子效果的 3D 场景。项目包括:

  • 卡片的动态进入与点击旋转动画
  • 背景粒子效果
  • 通过鼠标交互实现卡片旋转

请添加图片描述

HTML 和 CSS

HTML 文件是项目的基础结构,用于引入相关的依赖和定义渲染 3D 场景的容器:

  1. 页面结构
    • <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>
  1. 页面样式
    • 清除浏览器的默认样式,确保各个浏览器渲染一致。
    • 设置页面背景为深灰色,突出 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 让相机位于场景的前方。
  • 灯光

    • 环境光 (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/

;