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.

231 lines
5.5 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
class="principle-diagram"
v-if="showPrinciple"
:class="{ 'principle-only': showPrincipleOnly }"
>
<img src="/models/transformer/yuanli.png" alt="原理图" class="principle-img" />
</div>
</div>
<!-- 固定位置的控制按钮 -->
<div class="control-buttons">
<el-button type="primary" @click="toggle3D">三维图切换</el-button>
<el-button type="primary" @click="togglePrinciple">原理图切换</el-button>
<el-button type="primary" @click="showBoth">三维和原理图</el-button>
</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 showPrinciple = ref(false);
const showPrincipleOnly = ref(false); // 只显示原理图模式
// 切换显示状态
const toggle3D = () => {
console.log('点击三维图切换');
show3D.value = true;
showPrinciple.value = false;
showPrincipleOnly.value = false;
console.log('show3D:', show3D.value, 'showPrinciple:', showPrinciple.value);
};
const togglePrinciple = () => {
console.log('点击原理图切换');
showPrinciple.value = true;
show3D.value = false;
showPrincipleOnly.value = true;
console.log('show3D:', show3D.value, 'showPrinciple:', showPrinciple.value);
};
const showBoth = () => {
show3D.value = true;
showPrinciple.value = true;
showPrincipleOnly.value = false;
};
//相机
const camera = new PerspectiveCamera(
25,
div3D.value?.clientWidth! / div3D.value?.clientHeight!,
0.05,
1000
); // 初始比例设为1
camera.position.set(35, 10, 20);
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[] = [];
loader.load('/models/transformer/transformer.gltf', (gltf: any) => {
gltf.scene.position.y = -3;
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>