Vue项目里用Three.js搞个能点能转的3D集装箱,从场景搭建到交互完整走一遍
·
Vue与Three.js实战:打造可交互3D集装箱可视化系统
在物流管理和仓储系统的数字化浪潮中,3D可视化技术正成为提升操作效率的关键工具。本文将带您从零开始,在Vue项目中构建一个具备完整交互功能的3D集装箱系统,实现点击查看详情、自由旋转查看等实用功能。不同于基础教程,我们更关注工程化实践,解决实际开发中的场景管理、性能优化和业务逻辑整合问题。
1. 环境搭建与基础架构
1.1 项目初始化与依赖安装
首先创建Vue 3项目并安装必要依赖:
npm create vue@latest vue-3d-container
cd vue-3d-container
npm install three @types/three three-stdlib
关键依赖说明:
three:Three.js核心库@types/three:TypeScript类型定义three-stdlib:包含OrbitControls等扩展工具
1.2 场景基础结构设计
在 components/ContainerScene.vue 中建立核心结构:
<template>
<div ref="container" class="scene-container"></div>
</template>
<script setup>
import { ref, onMounted, onBeforeUnmount } from 'vue'
import * as THREE from 'three'
import { OrbitControls } from 'three/addons/controls/OrbitControls.js'
const container = ref(null)
let scene, camera, renderer, controls
const initScene = () => {
// 场景初始化逻辑将在这里实现
}
</script>
<style scoped>
.scene-container {
width: 100%;
height: 100vh;
background: #f0f2f5;
}
</style>
2. 集装箱模型构建与材质处理
2.1 基础几何体创建
集装箱本质上是经过特殊处理的立方体。我们通过组合不同材质创建逼真效果:
const createContainer = (size) => {
const geometry = new THREE.BoxGeometry(size.length, size.height, size.width)
// 六面不同材质
const materials = [
new THREE.MeshStandardMaterial({ color: 0x156289 }), // 右
new THREE.MeshStandardMaterial({ color: 0x156289 }), // 左
new THREE.MeshStandardMaterial({ color: 0x1a7fcc }), // 上
new THREE.MeshStandardMaterial({ color: 0x1a7fcc }), // 下
new THREE.MeshStandardMaterial({
color: 0xffffff,
metalness: 0.5,
roughness: 0.4
}), // 前
new THREE.MeshStandardMaterial({ color: 0x1a7fcc }) // 后
]
const container = new THREE.Mesh(geometry, materials)
container.userData.type = 'container'
return container
}
2.2 纹理贴图高级应用
使用纹理贴图提升真实感:
const loadTextures = async () => {
const textureLoader = new THREE.TextureLoader()
const materials = await Promise.all([
textureLoader.loadAsync('/textures/container_side.jpg'),
textureLoader.loadAsync('/textures/container_side.jpg'),
textureLoader.loadAsync('/textures/container_top.jpg'),
// 其他面纹理加载...
].map(t => t.then(tex => {
tex.flipY = false
return new THREE.MeshStandardMaterial({ map: tex })
})))
return materials
}
纹理处理技巧:
- 使用
loadAsync实现异步加载 - 设置
flipY=false避免图像上下颠倒 - 添加
roughnessMap和normalMap增强材质细节
3. 交互系统实现
3.1 相机控制与场景导航
集成OrbitControls实现基础观察控制:
const setupControls = () => {
controls = new OrbitControls(camera, renderer.domElement)
controls.enableDamping = true
controls.dampingFactor = 0.05
controls.screenSpacePanning = false
controls.minDistance = 100
controls.maxDistance = 2000
controls.maxPolarAngle = Math.PI
}
3.2 射线检测与对象交互
实现点击集装箱显示信息的功能:
const setupRaycaster = () => {
const raycaster = new THREE.Raycaster()
const mouse = new THREE.Vector2()
window.addEventListener('click', (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, true)
if (intersects.length > 0) {
const object = intersects[0].object
while (object.parent && !object.userData.type) {
object = object.parent
}
if (object.userData.type === 'container') {
showContainerInfo(object.userData.info)
}
}
})
}
4. 性能优化与高级特性
4.1 渲染性能优化策略
const optimizePerformance = () => {
// 1. 使用共享几何体
const containerGeometry = new THREE.BoxGeometry(10, 10, 10)
// 2. 合并相似材质
const materialCache = new Map()
// 3. 实现按需渲染
let renderRequested = false
const requestRender = () => {
if (!renderRequested) {
renderRequested = true
requestAnimationFrame(() => {
renderer.render(scene, camera)
renderRequested = false
})
}
}
controls.addEventListener('change', requestRender)
}
4.2 动态加载与LOD系统
实现根据距离动态调整细节:
const setupLOD = () => {
const lod = new THREE.LOD()
// 高模
const highDetail = createContainer(/* 参数 */)
lod.addLevel(highDetail, 50)
// 中模
const midDetail = createContainer(/* 简化参数 */)
lod.addLevel(midDetail, 150)
// 低模
const lowDetail = createContainer(/* 极简参数 */)
lod.addLevel(lowDetail, 300)
scene.add(lod)
}
5. 业务数据集成与可视化
5.1 数据绑定与状态管理
将Vue的响应式系统与Three.js结合:
import { watch } from 'vue'
import { useCargoStore } from '@/stores/cargo'
const cargoStore = useCargoStore()
watch(() => cargoStore.containers, (newContainers) => {
scene.children.filter(obj => obj.userData.type === 'container')
.forEach(obj => scene.remove(obj))
newContainers.forEach(container => {
const mesh = createContainer(container.dimensions)
mesh.position.set(container.position.x, container.position.y, container.position.z)
mesh.userData.info = container.info
scene.add(mesh)
})
}, { deep: true })
5.2 信息面板与UI集成
创建悬浮信息面板组件:
<template>
<div v-if="activeContainer" class="info-panel">
<h3>{{ activeContainer.id }}</h3>
<div class="info-grid">
<div>当前位置</div>
<div>{{ activeContainer.position }}</div>
<div>货物类型</div>
<div>{{ activeContainer.cargoType }}</div>
<!-- 其他信息字段 -->
</div>
</div>
</template>
<script setup>
import { ref } from 'vue'
const activeContainer = ref(null)
const showContainerInfo = (info) => {
activeContainer.value = info
}
</script>
<style scoped>
.info-panel {
position: absolute;
right: 20px;
top: 20px;
background: rgba(255, 255, 255, 0.9);
padding: 15px;
border-radius: 8px;
box-shadow: 0 2px 10px rgba(0, 0, 0, 0.1);
max-width: 300px;
}
.info-grid {
display: grid;
grid-template-columns: 100px 1fr;
gap: 8px;
}
</style>
6. 部署与生产环境优化
6.1 资源预加载策略
const preloadAssets = async () => {
const assets = [
'/models/container.glb',
'/textures/container_side.jpg',
'/textures/container_top.jpg',
// 其他资源
]
const loader = new THREE.TextureLoader()
const loadingManager = new THREE.LoadingManager()
loadingManager.onProgress = (url, loaded, total) => {
console.log(`加载进度: ${loaded}/${total} - ${url}`)
}
await Promise.all(assets.map(asset => {
if (asset.endsWith('.jpg') || asset.endsWith('.png')) {
return loader.loadAsync(asset)
}
// 其他类型资源加载
}))
}
6.2 响应式设计处理
适应不同屏幕尺寸:
const handleResize = () => {
camera.aspect = window.innerWidth / window.innerHeight
camera.updateProjectionMatrix()
renderer.setSize(window.innerWidth, window.innerHeight)
}
window.addEventListener('resize', () => {
handleResize()
// 防抖处理
clearTimeout(resizeTimer)
resizeTimer = setTimeout(() => {
renderer.render(scene, camera)
}, 200)
})
在真实项目中,这套系统已经帮助多个物流客户实现了仓储可视化管理的升级。一个特别实用的技巧是为不同状态的集装箱使用颜色编码:蓝色表示待装载,绿色表示运输中,红色表示需要检查。这种视觉提示显著提升了操作人员的效率。
更多推荐

所有评论(0)