You cannot select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

191 lines
4.3 KiB
Vue

This file contains ambiguous Unicode characters!

This file contains ambiguous Unicode characters that may be confused with others in your current locale. If your use case is intentional and legitimate, you can safely ignore this warning. Use the Escape button to highlight these characters.

<template>
<div class="container">
<!-- 排油注氮 -->
<!-- 3D 场景 -->
<div class="div3D" ref="div3D" v-show="show3D"></div>
</div>
</template>
<script setup lang="ts">
import { ref, onMounted, onUnmounted } from 'vue';
//导入 threejs
import {
AxesHelper,
Color,
PerspectiveCamera,
Scene,
WebGLRenderer,
EquirectangularReflectionMapping,
Group
} from 'three';
import { OrbitControls } from 'three/examples/jsm/controls/OrbitControls';
import { GLTFLoader } from 'three/examples/jsm/loaders/GLTFLoader';
import { RGBELoader } from 'three/examples/jsm/loaders/RGBELoader';
import { DRACOLoader } from 'three/examples/jsm/loaders/DRACOLoader';
const div3D = ref<HTMLDivElement>();
//场景
const scene = new Scene();
// 显示控制
const show3D = ref(true);
//相机
const camera = new PerspectiveCamera(
25,
div3D.value?.clientWidth! / div3D.value?.clientHeight!,
0.05,
1000
); // 初始比例设为1
camera.position.set(0, 14, 13.5);
camera.lookAt(0, 0, 0);
scene.add(camera);
//渲染器
const render = new WebGLRenderer({ antialias: true }); // 启用抗锯齿
render.setClearColor(new Color('#131519'));
//添加控制器
const controls = new OrbitControls(camera, render.domElement);
// 禁用滚轮缩放
// controls.enableZoom = false;
// controls.autoRotate = false;
// controls.enablePan = false; // 禁用右键平移
controls.autoRotateSpeed = 1;
controls.dampingFactor = 0.2;
controls.rotateSpeed = 0.3; // 降低旋转速度(默认为 1.0
controls.enableDamping = true;
controls.enableRotate = true;
controls.minDistance = 1; // 允许更近距离观察模型细节
// 设置视角限制向上45度向下45度
// 从正前方开始计算向上最大45度向下最大45度
controls.minPolarAngle = Math.PI / 4; // 向上最多45度 (π/4)
controls.maxPolarAngle = (Math.PI * 3) / 4; // 向下最多45度 (3π/4)
const loader = new GLTFLoader();
const dracoLoader = new DRACOLoader();
loader.setDRACOLoader(dracoLoader);
let meshArr: Group[] = [];
// F:\vue\workspace\maotu-webtopo\public\models\complexBuilding\complexBuilding.gltf
loader.load('/models/complexBuilding/complexBuilding.gltf', (gltf: any) => {
gltf.scene.position.y = -6;
scene.add(gltf.scene);
});
//环境贴图
const rgbELoader = new RGBELoader().load(
'/models/texture/Alex_Hart-Nature_Lab_Bones_2k.hdr',
(envMap: any) => {
envMap.mapping = EquirectangularReflectionMapping;
scene.environment = envMap;
}
);
//物体
// const axes = new AxesHelper(20);
// scene.add(axes);
// 监听容器大小变化
function handleResize() {
if (!div3D.value) return;
const container = div3D.value;
const width = container.clientWidth;
const height = container.clientHeight;
// 更新相机比例
camera.aspect = width / height;
camera.updateProjectionMatrix();
// 更新渲染器大小
render.setSize(width, height);
}
function init() {
requestAnimationFrame(init);
controls.update();
render.render(scene, camera);
}
onMounted(() => {
// 初始化时设置正确大小
handleResize();
// 启动渲染循环
init();
// 将渲染器添加到容器
div3D.value?.appendChild(render.domElement);
// 添加容器大小监听
const resizeObserver = new ResizeObserver(handleResize);
if (div3D.value) {
resizeObserver.observe(div3D.value);
}
// 保存观察器引用以便销毁
(window as any)._resizeObserver = resizeObserver;
});
onUnmounted(() => {
// 清理资源
if ((window as any)._resizeObserver) {
(window as any)._resizeObserver.disconnect();
delete (window as any)._resizeObserver;
}
});
</script>
<style scoped>
.container {
width: 100%;
height: 100%;
position: relative;
}
.div3D {
width: 100%;
height: 100%;
position: relative;
}
.principle-diagram {
position: absolute;
top: 10%;
right: 0;
width: 45%;
height: 45%;
z-index: 50;
display: flex;
justify-content: center;
align-items: center;
/* transition: all 0.3s ease; */
}
/* 只显示原理图时的样式 */
.principle-diagram.principle-only {
top: 0;
right: 0;
width: 100%;
height: 100%;
border-radius: 0;
box-shadow: none;
}
.principle-img {
max-width: 100%;
max-height: 100%;
object-fit: contain;
}
.control-buttons {
position: absolute;
top: 2%;
left: 50%;
transform: translateX(-50%);
display: flex;
flex-direction: row;
gap: 0.5%;
z-index: 100;
}
</style>