javascript
复制代码
<template>
<div ref="threeJsContainer" class="three-js-container"></div>
</template>
<script>
import * as THREE from 'three';
import { GLTFLoader } from 'three/examples/jsm/loaders/GLTFLoader';
// 引入轨道控制器扩展库OrbitControls.js
import { OrbitControls } from 'three/examples/jsm/controls/OrbitControls.js';
export default {
props: {
// glb模型的网络地址
glbUrl: {
type: String,
required: true
}
},
data() {
return {
scene: null,
camera: null,
renderer: null,
glbModel: null,
outerLine: null, // 外侧边框线,长方体
};
},
mounted() {
this.$nextTick(() => {
this.initThree();
})
},
methods: {
initThree() {
// 创建场景
this.scene = new THREE.Scene();
this.scene.background = new THREE.Color(0xeeeeee);
const width = 360; //宽度
const height = 360; //高度
// 创建灯光
const ambientLight = new THREE.AmbientLight(0xffffff, 1);
this.scene.add(ambientLight);
const directionalLight = new THREE.DirectionalLight(0xffffff, 1);
directionalLight.position.set(1, 1, 1);
this.scene.add(directionalLight);
// 创建相机
this.camera = new THREE.PerspectiveCamera(45, width / height, 1, 3000);
// 创建渲染器
this.renderer = new THREE.WebGLRenderer();
this.renderer.setSize(width, height);
// 加载GLB模型
this.loadGLBModel();
this.$refs.threeJsContainer.appendChild(this.renderer.domElement);
// 设置相机控件轨道控制器OrbitControls
const controls = new OrbitControls(this.camera, this.renderer.domElement);
// 如果OrbitControls改变了相机参数,重新调用渲染器渲染三维场景
controls.addEventListener('change', () => {
this.renderer.render(this.scene, this.camera); //执行渲染操作
});//监听鼠标、键盘事件
},
// 加载GLB模型
loadGLBModel() {
const loader = new GLTFLoader();
loader.load(this.glbUrl, (gltf) => {
this.glbModel = gltf.scene;
const box = new THREE.Box3().setFromObject(gltf.scene);
// 计算glb的长宽高
const glbSize = {
x: Number((box.max.x - box.min.x).toFixed(2)),
y: Number((box.max.y - box.min.y).toFixed(2)),
z: Number((box.max.z - box.min.z).toFixed(2))
}
// 绘制边框线,默认不显示
this.drawBorder(box.min, box.max)
this.$emit('getSize', glbSize)
this.scene.add(this.glbModel);
// 动态计算相机位置,保持中心始终看到大小相同的模型,todo: 待优化
const { x, y, z } = glbSize
console.log('glbSize', glbSize)
this.camera.position.set(-x * 1.8, y * 1.4, z * 2);
this.camera.lookAt(0, 0, 0);
this.animate(this.renderer, this.scene, this.camera);
});
},
// 画模型的边框线
drawBorder(min, max) {
// 定义长方体的8个顶点坐标
const vertices = new Float32Array([
// 顶面四个顶点
min.x, max.y, min.z, // 0
max.x, max.y, min.z, // 1
max.x, max.y, max.z, // 2
min.x, max.y, max.z, // 3
// 底面四个顶点
min.x, min.y, min.z, // 4
max.x, min.y, min.z, // 5
max.x, min.y, max.z, // 6
min.x, min.y, max.z // 7
]);
// 定义顶点索引,用于构成长方体的12条边
const indices = new Uint16Array([
0, 1, 1, 2, 2, 3, 3, 0, // 顶面边
4, 5, 5, 6, 6, 7, 7, 4, // 底面边
0, 4, 1, 5, 2, 6, 3, 7 // 竖直边
]);
// 创建一个BufferGeometry
const geometry = new THREE.BufferGeometry();
geometry.setAttribute('position', new THREE.BufferAttribute(vertices, 3));
geometry.setIndex(new THREE.BufferAttribute(indices, 1));
// 创建一个材质
const material = new THREE.LineBasicMaterial({ color: 0xfdaf58 });
// 创建线段
this.outerLine = new THREE.LineSegments(geometry, material);
// 初始隐藏,点击那个玩意的时候再显示
this.outerLine.visible = false;
// 将线段添加到场景中
this.scene.add(this.outerLine);
},
animate(renderer, scene, camera) {
requestAnimationFrame(() => this.animate(renderer, scene, camera));
renderer.render(scene, camera);
},
// 显示隐藏边框线
toggleShowOuterLine(flag) {
this.outerLine.visible = flag;
}
},
};
</script>
<style scoped>
.three-js-container {
width: 360px;
height: 360px;
}
</style>