vue3+three.js实现3d模型的展示
·
前言:
前端通过three工具来实现3d模型的展示
实现效果:

安装插件:
npm i three @types/three

实现代码:
模型放到public里面
https://dtmall-tel.alicdn.com/edgeComputingConfig/upload_models/1591673169101/RobotExpressive.glb

加载glb文件
const loader = new GLTFLoader();
const modelUrl = '/model/demo.glb';
初始化创建画布,画布设置背景颜色
// 初始化场景
scene = new THREE.Scene();
scene.background = new THREE.Color('#f8f8f8');//大背景const width = container.value.clientWidth;
const height = container.value.clientHeight;
源码:
<template>
<div id="container" ref="container"></div>
</template>
<script setup>
import * as THREE from 'three';
import { GLTFLoader } from 'three/examples/jsm/loaders/GLTFLoader';
import { OrbitControls } from 'three/examples/jsm/controls/OrbitControls';
import { ref, onMounted, onUnmounted, shallowRef } from 'vue';
// 使用 shallowRef 或普通变量避免深度响应式
const container = ref(null);
// 使用普通变量而不是 ref
let scene = null;
let camera = null;
let renderer = null;
let controls = null;
let mixer = null;
let animationId = null;
const initThree = () => {
try {
if (!container.value) return;
// 初始化场景
scene = new THREE.Scene();
scene.background = new THREE.Color('#f8f8f8');//大背景
const width = container.value.clientWidth;
const height = container.value.clientHeight;
// 初始化相机
camera = new THREE.PerspectiveCamera(75, width / height, 0.1, 1000);
camera.position.set(0, 0, 5);
// 初始化渲染器
renderer = new THREE.WebGLRenderer({
antialias: true,
alpha: true
});
renderer.setSize(width, height);
renderer.outputColorSpace = THREE.SRGBColorSpace;
renderer.toneMapping = THREE.ACESFilmicToneMapping;
container.value.innerHTML = '';
container.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(1, 1, 1);
scene.add(directionalLight);
// 添加轨道控制
controls = new OrbitControls(camera, renderer.domElement);
controls.enableDamping = true;
controls.dampingFactor = 0.05;
// 加载模型
loadModel();
// 启动动画循环
animate();
} catch (error) {
console.error('初始化Three.js时发生错误:', error);
}
}
const loadModel = () => {
const loader = new GLTFLoader();
const modelUrl = '/model/demo.glb';
loader.load(
modelUrl,
(gltf) => {
console.log('模型加载成功', gltf);
const model = gltf.scene;
scene.add(model);
// 调整模型位置和缩放
model.position.set(0, -1, 0);
model.scale.set(1, 1, 1);
// 设置动画混合器
if (gltf.animations && gltf.animations.length) {
mixer = new THREE.AnimationMixer(model);
gltf.animations.forEach((clip) => {
mixer.clipAction(clip).play();
});
}
controls.update();
},
(xhr) => {
const progress = (xhr.loaded / xhr.total) * 100;
console.log(`加载进度: ${progress.toFixed(2)}%`);
},
(error) => {
console.error('模型加载失败:', error);
// 创建替代几何体
const geometry = new THREE.BoxGeometry(1, 1, 1);
const material = new THREE.MeshBasicMaterial({
color: 0x00ff00,
wireframe: true
});
const cube = new THREE.Mesh(geometry, material);
scene.add(cube);
}
);
}
const animate = () => {
animationId = requestAnimationFrame(animate);
// 更新动画混合器
if (mixer) {
const delta = 0.016;
mixer.update(delta);
}
// 更新控制器
if (controls) {
controls.update();
}
// 渲染场景
if (renderer && scene && camera) {
renderer.render(scene, camera);
}
}
const onWindowResize = () => {
if (!container.value || !camera || !renderer) return;
const width = container.value.clientWidth;
const height = container.value.clientHeight;
camera.aspect = width / height;
camera.updateProjectionMatrix();
renderer.setSize(width, height);
}
const cleanup = () => {
// 停止动画循环
if (animationId) {
cancelAnimationFrame(animationId);
}
// 清理 Three.js 资源
if (renderer) {
renderer.dispose();
// 清理 DOM
if (renderer.domElement && renderer.domElement.parentNode) {
renderer.domElement.parentNode.removeChild(renderer.domElement);
}
}
// 清理场景
if (scene) {
scene.traverse((object) => {
if (object.isMesh) {
if (object.geometry) {
object.geometry.dispose();
}
if (object.material) {
if (Array.isArray(object.material)) {
object.material.forEach(material => material.dispose());
} else {
object.material.dispose();
}
}
}
});
}
}
onMounted(() => {
// 使用 nextTick 确保 DOM 已渲染
setTimeout(() => {
initThree();
}, 100);
window.addEventListener('resize', onWindowResize);
});
onUnmounted(() => {
window.removeEventListener('resize', onWindowResize);
cleanup();
});
</script>
<style lang="scss" scoped>
#container {
width: 100%;
height: 400px;
border: 1px solid #ccc;
}
#container > canvas {
display: block;
width: 100% !important;
height: 100% !important;
}
</style>
更多推荐



所有评论(0)