在现代Web开发中,结合Vue.js的响应式特性与Three.js的强大3D渲染能力,可以创造出令人印象深刻的3D动画效果。本篇博客将介绍如何使用Vue 3、TypeScript和Three.js来封装一个可重用的3D动画框架。
1. 介绍
Vue 3
Vue 3 是Vue框架的最新版本,它引入了许多改进,包括更好的性能、更简洁的API以及新的响应性系统。
TypeScript
TypeScript 是JavaScript的一个超集,它可以提供静态类型检查和编译时错误检测,有助于大型项目的维护和开发效率提升。
Three.js
Three.js 是一个基于WebGL的3D图形库,它简化了复杂的WebGL API,使得开发者能够更容易地创建和显示3D图形。
2. 开发环境准备
确保安装了以下软件:
- Node.js(推荐使用最新的LTS版本)
- npm 或 yarn
安装必要的包:
bash
深色版本
1npm install -g @vue/cli
2npm install
初始化Vue 3项目:
bash
深色版本
1vue create my-project
2cd my-project
选择使用TypeScript和Vue 3的选项。然后安装Three.js:
bash
深色版本
1npm install three
3. 创建基本的3D组件
我们从创建一个基础的3D组件开始,这个组件可以作为所有3D元素的基础。
3.1 创建3D组件
在 src/components/
目录下创建 Base3DComponent.vue
文件:
vue
深色版本
1<template>
2 <div ref="containerRef" />
3</template>
4
5<script lang="ts">
6import { defineComponent, onMounted, onUnmounted, ref } from 'vue';
7import * as THREE from 'three';
8
9export default defineComponent({
10 name: 'Base3DComponent',
11 setup() {
12 const containerRef = ref<HTMLDivElement | null>(null);
13
14 let renderer: THREE.WebGLRenderer | null = null;
15 let scene: THREE.Scene | null = null;
16 let camera: THREE.PerspectiveCamera | null = null;
17
18 const init = () => {
19 renderer = new THREE.WebGLRenderer({ antialias: true });
20 renderer.setSize(containerRef.value!.clientWidth, containerRef.value!.clientHeight);
21 renderer.setPixelRatio(window.devicePixelRatio);
22 containerRef.value!.appendChild(renderer.domElement);
23
24 scene = new THREE.Scene();
25
26 camera = new THREE.PerspectiveCamera(
27 75,
28 containerRef.value!.clientWidth / containerRef.value!.clientHeight,
29 0.1,
30 1000
31 );
32 camera.position.z = 5;
33
34 const geometry = new THREE.BoxGeometry(1, 1, 1);
35 const material = new THREE.MeshBasicMaterial({ color: 0x00ff00 });
36 const cube = new THREE.Mesh(geometry, material);
37 scene!.add(cube);
38
39 const animate = () => {
40 requestAnimationFrame(animate);
41 if (cube) cube.rotation.x += 0.01;
42 if (cube) cube.rotation.y += 0.01;
43 if (renderer) renderer.render(scene!, camera!);
44 };
45
46 animate();
47 };
48
49 onMounted(() => {
50 if (containerRef.value) {
51 init();
52 }
53 });
54
55 onUnmounted(() => {
56 if (renderer) {
57 renderer.dispose();
58 }
59 });
60
61 return {
62 containerRef,
63 };
64 },
65});
66</script>
3.2 使用3D组件
在 App.vue
中使用 Base3DComponent
组件:
vue
深色版本
1<template>
2 <div id="app">
3 <base-3d-component />
4 </div>
5</template>
6
7<script lang="ts">
8import Base3DComponent from './components/Base3DComponent.vue';
9
10export default {
11 components: {
12 Base3DComponent,
13 },
14};
15</script>
4. 封装高级3D组件
4.1 创建可配置的3D组件
让我们扩展 Base3DComponent
,使其可以接受属性来配置不同的3D对象。
更新 Base3DComponent.vue
vue
深色版本
1<template>
2 <div ref="containerRef" />
3</template>
4
5<script lang="ts">
6import { defineComponent, onMounted, onUnmounted, ref } from 'vue';
7import * as THREE from 'three';
8
9export default defineComponent({
10 name: 'Base3DComponent',
11 props: {
12 width: {
13 type: Number,
14 default: 1,
15 },
16 height: {
17 type: Number,
18 default: 1,
19 },
20 depth: {
21 type: Number,
22 default: 1,
23 },
24 color: {
25 type: String,
26 default: '#00ff00',
27 },
28 },
29 setup(props) {
30 const containerRef = ref<HTMLDivElement | null>(null);
31
32 let renderer: THREE.WebGLRenderer | null = null;
33 let scene: THREE.Scene | null = null;
34 let camera: THREE.PerspectiveCamera | null = null;
35
36 const init = () => {
37 renderer = new THREE.WebGLRenderer({ antialias: true });
38 renderer.setSize(containerRef.value!.clientWidth, containerRef.value!.clientHeight);
39 renderer.setPixelRatio(window.devicePixelRatio);
40 containerRef.value!.appendChild(renderer.domElement);
41
42 scene = new THREE.Scene();
43
44 camera = new THREE.PerspectiveCamera(
45 75,
46 containerRef.value!.clientWidth / containerRef.value!.clientHeight,
47 0.1,
48 1000
49 );
50 camera.position.z = 5;
51
52 const geometry = new THREE.BoxGeometry(props.width, props.height, props.depth);
53 const material = new THREE.MeshBasicMaterial({ color: props.color });
54 const cube = new THREE.Mesh(geometry, material);
55 scene!.add(cube);
56
57 const animate = () => {
58 requestAnimationFrame(animate);
59 if (cube) cube.rotation.x += 0.01;
60 if (cube) cube.rotation.y += 0.01;
61 if (renderer) renderer.render(scene!, camera!);
62 };
63
64 animate();
65 };
66
67 onMounted(() => {
68 if (containerRef.value) {
69 init();
70 }
71 });
72
73 onUnmounted(() => {
74 if (renderer) {
75 renderer.dispose();
76 }
77 });
78
79 return {
80 containerRef,
81 };
82 },
83});
84</script>
4.2 使用配置化的3D组件
在 App.vue
中使用带有属性的 Base3DComponent
:
vue
深色版本
1<template>
2 <div id="app">
3 <base-3d-component :width="2" :height="2" :depth="2" :color="#ff0000" />
4 </div>
5</template>
6
7<script lang="ts">
8import Base3DComponent from './components/Base3DComponent.vue';
9
10export default {
11 components: {
12 Base3DComponent,
13 },
14};
15</script>
5. 封装可复用的3D动画框架
为了更好地管理3D对象和动画,我们可以进一步抽象出一个更通用的3D框架。
5.1 创建3D框架类
创建一个 src/utils/ThreeFramework.ts
文件:
typescript
深色版本
1import * as THREE from 'three';
2
3export class ThreeFramework {
4 private scene: THREE.Scene;
5 private camera: THREE.PerspectiveCamera;
6 private renderer: THREE.WebGLRenderer;
7 private container: HTMLDivElement;
8
9 constructor(container: HTMLDivElement, width: number, height: number) {
10 this.container = container;
11 this.scene = new THREE.Scene();
12 this.camera = new THREE.PerspectiveCamera(75, width / height, 0.1, 1000);
13 this.camera.position.z = 5;
14
15 this.renderer = new THREE.WebGLRenderer({ antialias: true });
16 this.renderer.setSize(width, height);
17 this.renderer.setPixelRatio(window.devicePixelRatio);
18 container.appendChild(this.renderer.domElement);
19 }
20
21 public addMesh(mesh: THREE.Mesh): void {
22 this.scene.add(mesh);
23 }
24
25 public removeMesh(mesh: THREE.Mesh): void {
26 this.scene.remove(mesh);
27 }
28
29 public start(): void {
30 const animate = () => {
31 requestAnimationFrame(animate);
32 this.render();
33 };
34 animate();
35 }
36
37 private render(): void {
38 this.renderer.render(this.scene, this.camera);
39 }
40}
5.2 更新3D组件以使用框架
修改 Base3DComponent.vue
,以使用 ThreeFramework
类:
vue
深色版本
1<template>
2 <div ref="containerRef" />
3</template>
4
5<script lang="ts">
6import { defineComponent, onMounted, onUnmounted, ref } from 'vue';
7import * as THREE from 'three';
8import { ThreeFramework } from '../utils/ThreeFramework';
9
10export default defineComponent({
11 name: 'Base3DComponent',
12 props: {
13 width: {
14 type: Number,
15 default: 1,
16 },
17 height: {
18 type: Number,
19 default: 1,
20 },
21 depth: {
22 type: Number,
23 default: 1,
24 },
25 color: {
26 type: String,
27 default: '#00ff00',
28 },
29 },
30 setup(props) {
31 const containerRef = ref<HTMLDivElement | null>(null);
32
33 let framework: ThreeFramework | null = null;
34 let cube: THREE.Mesh | null = null;
35
36 const init = () => {
37 framework = new ThreeFramework(containerRef.value!, window.innerWidth, window.innerHeight);
38
39 const geometry = new THREE.BoxGeometry(props.width, props.height, props.depth);
40 const material = new THREE.MeshBasicMaterial({ color: props.color });
41 cube = new THREE.Mesh(geometry, material);
42
43 framework.addMesh(cube);
44
45 framework.start();
46
47 const animate = () => {
48 requestAnimationFrame(animate);
49 if (cube) cube.rotation.x += 0.01;
50 if (cube) cube.rotation.y += 0.01;
51 };
52
53 animate();
54 };
55
56 onMounted(() => {
57 if (containerRef.value) {
58 init();
59 }
60 });
61
62 onUnmounted(() => {
63 if (framework) {
64 framework.removeMesh(cube!);
65 }
66 });
67
68 return {
69 containerRef,
70 };
71 },
72});
73</script>
6. 总结
通过上述步骤,我们创建了一个基于Vue 3、TypeScript和Three.js的3D动画框架。这个框架可以方便地添加、移除3D对象,并且支持自定义配置。你可以在此基础上继续扩展更多的功能,如用户交互、碰撞检测等。