在 Vue 中使用 Three.js 加载模型、控制视角、添加点击事件是构建 3D 场景的常见需求。下面是一个完整的示例,演示如何在 Vue 单文件组件中实现以下功能:
- 使用
GLTFLoader
加载.glb/.gltf
模型 - 添加
OrbitControls
控制视角(旋转、缩放、平移) - 给模型添加点击事件
使用的技术栈
- Vue 3 + Composition API(或 Vue 2)
- Three.js 核心库
three/examples/js/loaders/GLTFLoader
three/examples/js/controls/OrbitControls
📦 安装依赖(如未安装)
npm install three
npm install three-gltf-loader # 或直接引入 GLTFLoader
示例代码:Vue 单文件组件
<template>
<div class="model-viewer-container" ref="viewerContainer"></div>
</template>
<script setup>
import { ref, onMounted } from 'vue'
import * as THREE from 'three'
import { OrbitControls } from 'three/addons/controls/OrbitControls.js'
import { GLTFLoader } from 'three/addons/loaders/GLTFLoader.js'
const viewerContainer = ref(null)
let scene, camera, renderer, controls, model
function init() {
// 创建场景
scene = new THREE.Scene()
scene.background = new THREE.Color(0xeeeeee)
// 创建相机
const width = viewerContainer.value.clientWidth
const height = viewerContainer.value.clientHeight
camera = new THREE.PerspectiveCamera(45, width / height, 0.1, 1000)
camera.position.set(0, 2, 5)
// 创建渲染器
renderer = new THREE.WebGLRenderer({ antialias: true })
renderer.setSize(width, height)
renderer.setPixelRatio(window.devicePixelRatio)
viewerContainer.value.appendChild(renderer.domElement)
// 添加光源
const ambientLight = new THREE.AmbientLight(0xffffff, 0.6)
scene.add(ambientLight)
const directionalLight = new THREE.DirectionalLight(0xffffff, 0.8)
directionalLight.position.set(5, 10, 7.5)
scene.add(directionalLight)
// 添加控制器
controls = new OrbitControls(camera, renderer.domElement)
controls.enableDamping = true
// 加载模型
const loader = new GLTFLoader()
loader.load(
'/models/test.glb', // 替换为你的模型路径
(gltf) => {
model = gltf.scene
scene.add(model)
// 添加点击事件监听
window.addEventListener('click', onClick)
},
undefined,
(error) => {
console.error('An error occurred while loading the model:', error)
}
)
// 渲染循环
function animate() {
requestAnimationFrame(animate)
controls.update()
renderer.render(scene, camera)
}
animate()
}
// 点击事件处理函数
function onClick(event) {
if (!model) return
// 计算鼠标归一化坐标
const mouse = new THREE.Vector2()
mouse.x = (event.clientX / window.innerWidth) * 2 - 1
mouse.y = -(event.clientY / window.innerHeight) * 2 + 1
// 创建射线
const raycaster = new THREE.Raycaster()
raycaster.setFromCamera(mouse, camera)
// 获取模型中的所有可交互对象
const intersects = raycaster.intersectObject(model, true)
if (intersects.length > 0) {
console.log('点击了模型!', intersects[0].object)
alert('你点击了模型上的一个部件')
}
}
// 响应窗口变化
window.addEventListener('resize', () => {
if (!camera || !renderer) return
camera.aspect = viewerContainer.value.clientWidth / viewerContainer.value.clientHeight
camera.updateProjectionMatrix()
renderer.setSize(viewerContainer.value.clientWidth, viewerContainer.value.clientHeight)
})
onMounted(() => {
init()
})
</script>
<style scoped>
.model-viewer-container {
width: 100%;
height: 100vh;
}
</style>
文件结构建议
your-project/
├── public/
│ └── models/
│ └── test.glb <-- 放置你的模型文件
├── src/
│ └── components/
│ └── ModelViewer.vue
注意:模型放在
public/models/
目录下,通过/models/test.glb
路径访问。
🔧 功能说明
功能 | 实现方式 |
---|---|
加载模型 | 使用 GLTFLoader 加载 .glb 或 .gltf 模型 |
控制视角 | 使用 OrbitControls 实现自由旋转、缩放、平移 |
点击事件 | 使用 Raycaster 进行射线检测,判断是否点击到模型 |
响应式布局 | 监听 resize 事件并更新相机和渲染器尺寸 |
扩展建议
需求 | 推荐做法 |
---|---|
多个模型加载 | 使用 Promise.all() 异步加载多个模型 |
模型动画播放 | 使用 AnimationMixer 和 Clock 控制动画 |
加载进度条 | 使用 LoadingManager 显示加载百分比 |
自定义材质 | 遍历模型子对象并修改材质颜色、透明度等属性 |
高亮选中部分 | 修改点击对象的材质颜色或使用 OutlinePass 后期高亮 |