一文万字详解three.js+vue3.js实现教程
作者:前端小崔
引言:Web3D技术的崛起
随着Web技术的快速发展,Web3D应用已经从简单的展示发展到复杂的交互体验,广泛应用于电商展示、游戏开发、数据可视化和教育培训等领域。Three.js作为最流行的WebGL库,让开发者能够轻松创建3D场景。而Vue3的响应式系统和组件化架构,为管理复杂3D场景提供了优雅的解决方案。
本文将详细介绍如何在Vue3项目中集成Three.js,从基础设置到高级功能实现,带你构建一个完整的3D场景。
一、项目环境搭建
1. 创建Vue3项目
npm init vue@latest # 选择Vue3 + TypeScript + Vite cd your-project-name npm install
2. 安装Three.js及相关库
npm install three @types/three three-stdlib
3. 项目结构
src/
├── assets/
├── components/
│ ├── ThreeScene.vue # 3D场景组件
│ ├── SceneControls.vue # 场景控制组件
│ └── ModelLoader.vue # 模型加载组件
├── composables/
│ └── useThree.js # Three.js组合式函数
├── views/
│ └── HomeView.vue # 主视图
└── main.js
二、Three.js核心概念
在集成之前,先了解Three.js的核心概念:
- 场景(Scene) :所有3D对象的容器
- 相机(Camera) :定义用户可见的视角
- 渲染器(Renderer) :将场景渲染到canvas元素
- 几何体(Geometry) :物体的形状
- 材质(Material) :物体表面的视觉特性
- 网格(Mesh) :几何体+材质的组合
- 光源(Light) :照亮场景的光源
- 动画循环(Animation Loop) :实现动态效果
三、在Vue3中创建基础3D场景
1. 创建ThreeScene组件
<template>
<div ref="sceneContainer" class="scene-container"></div>
</template>
<script setup>
import { ref, onMounted, onUnmounted } from 'vue';
import * as THREE from 'three';
import { OrbitControls } from 'three-stdlib/controls/OrbitControls';
const sceneContainer = ref(null);
// 核心Three.js对象
let scene, camera, renderer, controls, cube;
// 初始化场景
function initScene() {
// 1. 创建场景
scene = new THREE.Scene();
scene.background = new THREE.Color(0x1a1a2e);
// 2. 创建相机
camera = new THREE.PerspectiveCamera(
75,
window.innerWidth / window.innerHeight,
0.1,
1000
);
camera.position.z = 5;
// 3. 创建渲染器
renderer = new THREE.WebGLRenderer({
antialias: true,
alpha: true
});
renderer.setSize(window.innerWidth, window.innerHeight);
renderer.setPixelRatio(Math.min(window.devicePixelRatio, 2));
// 4. 添加到DOM
sceneContainer.value.appendChild(renderer.domElement);
// 5. 添加轨道控制器
controls = new OrbitControls(camera, renderer.domElement);
controls.enableDamping = true;
controls.dampingFactor = 0.05;
// 6. 添加光源
const ambientLight = new THREE.AmbientLight(0xffffff, 0.6);
scene.add(ambientLight);
const directionalLight = new THREE.DirectionalLight(0xffffff, 0.8);
directionalLight.position.set(5, 5, 5);
scene.add(directionalLight);
// 7. 创建立方体
const geometry = new THREE.BoxGeometry(1, 1, 1);
const material = new THREE.MeshStandardMaterial({
color: 0x00a8ff,
metalness: 0.7,
roughness: 0.2
});
cube = new THREE.Mesh(geometry, material);
scene.add(cube);
// 8. 添加网格地板
const floorGeometry = new THREE.PlaneGeometry(10, 10);
const floorMaterial = new THREE.MeshStandardMaterial({
color: 0x393e46,
side: THREE.DoubleSide
});
const floor = new THREE.Mesh(floorGeometry, floorMaterial);
floor.rotation.x = Math.PI / 2;
floor.position.y = -1.5;
scene.add(floor);
// 9. 开始动画循环
animate();
}
// 动画循环
function animate() {
requestAnimationFrame(animate);
// 立方体旋转
if (cube) {
cube.rotation.x += 0.01;
cube.rotation.y += 0.01;
}
// 更新控制器
if (controls) controls.update();
// 渲染场景
renderer.render(scene, camera);
}
// 响应式调整大小
function onWindowResize() {
camera.aspect = window.innerWidth / window.innerHeight;
camera.updateProjectionMatrix();
renderer.setSize(window.innerWidth, window.innerHeight);
}
onMounted(() => {
initScene();
window.addEventListener('resize', onWindowResize);
});
onUnmounted(() => {
window.removeEventListener('resize', onWindowResize);
// 清理资源
if (renderer) renderer.dispose();
});
</script>
<style scoped>
.scene-container {
width: 100%;
height: 100vh;
position: relative;
overflow: hidden;
}
</style>
2. 在主页面中使用组件
<template>
<div class="home-view">
<ThreeScene />
<div class="overlay">
<h1>Vue3 + Three.js 3D场景</h1>
<p>旋转视角: 按住鼠标左键拖动</p>
<p>缩放场景: 使用鼠标滚轮</p>
</div>
</div>
</template>
<script setup>
import ThreeScene from '@/components/ThreeScene.vue';
</script>
<style>
.home-view {
position: relative;
height: 100vh;
}
.overlay {
position: absolute;
top: 20px;
left: 20px;
color: white;
background: rgba(0, 0, 0, 0.5);
padding: 15px;
border-radius: 10px;
max-width: 300px;
z-index: 10;
}
</style>
四、高级功能实现
1. 模型加载与展示
<template>
<div class="model-loader">
<button @click="loadModel">加载模型</button>
<select v-model="selectedModel">
<option value="robot">机器人</option>
<option value="car">汽车</option>
</select>
</div>
</template>
<script setup>
import { ref } from 'vue';
import { GLTFLoader } from 'three-stdlib/loaders/GLTFLoader';
const emit = defineEmits(['model-loaded']);
const selectedModel = ref('robot');
async function loadModel() {
const loader = new GLTFLoader();
try {
const modelPath = selectedModel.value === 'robot'
? '/models/robot.glb'
: '/models/car.glb';
const gltf = await loader.loadAsync(modelPath);
// 调整模型大小和位置
gltf.scene.scale.set(0.5, 0.5, 0.5);
gltf.scene.position.y = -1;
// 发送到父组件
emit('model-loaded', gltf.scene);
} catch (error) {
console.error('模型加载失败:', error);
}
}
</script>
2. 在ThreeScene组件中处理模型加载
// ThreeScene.vue 中添加
import { onMounted } from 'vue';
// 添加模型状态
const currentModel = ref(null);
// 加载模型
function handleModelLoaded(model) {
// 移除旧模型
if (currentModel.value) {
scene.remove(currentModel.value);
}
// 添加新模型
scene.add(model);
currentModel.value = model;
}
3. 添加点击交互
// 在initScene函数中添加
function initRaycaster() {
const raycaster = new THREE.Raycaster();
const mouse = new THREE.Vector2();
function onMouseClick(event) {
// 计算鼠标位置
mouse.x = (event.clientX / window.innerWidth) * 2 - 1;
mouse.y = -(event.clientY / window.innerHeight) * 2 + 1;
// 更新射线
raycaster.setFromCamera(mouse, camera);
// 检测相交物体
const intersects = raycaster.intersectObjects(scene.children);
if (intersects.length > 0) {
const clickedObject = intersects[0].object;
// 高亮显示被点击的物体
clickedObject.material.emissive = new THREE.Color(0xffff00);
setTimeout(() => {
clickedObject.material.emissive = new THREE.Color(0x000000);
}, 1000);
console.log('点击了物体:', clickedObject);
}
}
window.addEventListener('click', onMouseClick);
onUnmounted(() => {
window.removeEventListener('click', onMouseClick);
});
}
// 在initScene中调用
initRaycaster();
五、性能优化技巧
1. 使用组合式函数复用逻辑
创建 src/composables/useThree.js:
import { ref, onUnmounted } from 'vue';
import * as THREE from 'three';
export function useThree(container) {
const scene = new THREE.Scene();
const camera = ref(null);
const renderer = ref(null);
function initCamera() {
camera.value = new THREE.PerspectiveCamera(
75,
window.innerWidth / window.innerHeight,
0.1,
1000
);
camera.value.position.z = 5;
}
function initRenderer() {
renderer.value = new THREE.WebGLRenderer({
antialias: true,
alpha: true
});
renderer.value.setSize(window.innerWidth, window.innerHeight);
renderer.value.setPixelRatio(Math.min(window.devicePixelRatio, 2));
container.value.appendChild(renderer.value.domElement);
}
function onWindowResize() {
camera.value.aspect = window.innerWidth / window.innerHeight;
camera.value.updateProjectionMatrix();
renderer.value.setSize(window.innerWidth, window.innerHeight);
}
function init() {
initCamera();
initRenderer();
window.addEventListener('resize', onWindowResize);
}
onUnmounted(() => {
window.removeEventListener('resize', onWindowResize);
if (renderer.value) renderer.value.dispose();
});
return {
scene,
camera,
renderer,
init
};
}
2. 在组件中使用组合式函数
// ThreeScene.vue 中
import { useThree } from '@/composables/useThree';
const { scene, camera, renderer } = useThree(sceneContainer);
onMounted(() => {
init(); // 调用组合式函数中的初始化
// 其他初始化代码...
});
3. 其他优化策略
- 模型压缩:使用glTF格式并启用Draco压缩
- 按需渲染:静态场景仅在变化时渲染
- LOD管理:根据距离显示不同细节模型
- 纹理优化:使用合适的分辨率和压缩格式
- 内存管理:及时释放不再使用的几何体和材质
六、调试工具与实用资源
1. 调试工具
- Three.js Inspector:浏览器扩展,实时检查场景
- Stats.js:性能监控面板
- dat.GUI:创建控制面板调整参数
2. 实用资源
- Three.js 官方文档
- glTF 模型库
- VueThreejs - Vue3的Three.js封装
七、总结
将Vue3与Three.js结合开发Web3D应用,可以充分发挥两者的优势:
Vue3的优势:
- 响应式状态管理
- 组件化架构
- 组合式API复用逻辑
- 丰富的生态系统
Three.js的优势:
- 强大的3D渲染能力
- 丰富的几何体和材质
- 物理效果支持
- 模型加载器
这种技术组合非常适合开发复杂的3D可视化应用、产品展示、教育模拟和游戏等场景。随着WebGPU等新技术的发展,Web3D应用的性能和表现力将进一步提升。
到此这篇关于three.js+vue3.js实现教程的文章就介绍到这了,更多相关three.js+vue3.js教程内容请搜索脚本之家以前的文章或继续浏览下面的相关文章希望大家以后多多支持脚本之家!
