添加三维模型(未完成)
This commit is contained in:
parent
8f1eb56a47
commit
900ca4dd2c
BIN
frontend/src/renderer/public/Model.glb
Normal file
BIN
frontend/src/renderer/public/Model.glb
Normal file
Binary file not shown.
BIN
frontend/src/renderer/src/assets/new/Model.glb
Normal file
BIN
frontend/src/renderer/src/assets/new/Model.glb
Normal file
Binary file not shown.
@ -231,7 +231,6 @@ import { useAuthStore } from '../stores/index.js'
|
||||
import Header from '@/views/Header.vue'
|
||||
import PatientCreate from '@/views/PatientCreate.vue'
|
||||
import Detection from '@/views/Detection.vue'
|
||||
import { color } from 'echarts'
|
||||
import PatientProfile from '@/views/PatientProfile.vue'
|
||||
const router = useRouter()
|
||||
const authStore = useAuthStore()
|
||||
|
||||
@ -85,7 +85,6 @@
|
||||
</el-button>
|
||||
</div>
|
||||
<div class="body-son-display">
|
||||
{{ }}
|
||||
<img src="@/assets/new/refresh.svg" alt="" style="margin-right: 8px;cursor: pointer;" title="重启IMU"
|
||||
@click="refreshClick('imu')">
|
||||
<div class="connecttext" :style="{ color: imuStatus == '已连接' ? '#00CC00' : '#808080' }">
|
||||
@ -128,7 +127,12 @@
|
||||
<span class="currencytext1">旋转角:</span>
|
||||
<span class="currencytext3">{{ headlist.rotation }}°</span>
|
||||
</div>
|
||||
<img src="@/assets/new/testheader.png" style="width: 100%;height: 80%;" alt="">
|
||||
<div style="width: 100%;height: 80%;" alt="">
|
||||
<!-- <img src="@/assets/new/testheader.png" > -->
|
||||
|
||||
<Model />
|
||||
</div>
|
||||
|
||||
</div>
|
||||
<div class="body-header-bottombox-right">
|
||||
<div class="body-header-bottombox-righttext" style="height: 20%;"></div>
|
||||
@ -433,6 +437,7 @@ import noImageSvg from '@/assets/no-image.svg'
|
||||
import DiagnosticMessage from '@/views/DiagnosticMessage.vue'
|
||||
import PatientCreate from '@/views/PatientCreate.vue'
|
||||
import HistoryDashboard from '@/views/PatientProfile.vue'
|
||||
import Model from './model.vue'
|
||||
|
||||
const emit = defineEmits([ 'endChange']);
|
||||
const asd = ref(0)
|
||||
|
||||
@ -1,5 +1,6 @@
|
||||
<template>
|
||||
<div class="login-page" :style="{ backgroundImage: `url(${bg})` }">
|
||||
<!-- <Model /> -->
|
||||
<!-- 页面主内容 -->
|
||||
<div class="login-content">
|
||||
<!-- 系统标题 -->
|
||||
@ -223,6 +224,7 @@ import { User, Lock, View, Hide, Phone } from '@element-plus/icons-vue'
|
||||
|
||||
import bg from '@/assets/new/newbg.jpg'
|
||||
import { getBackendUrl,systemAPI } from '../services/api.js'
|
||||
// import Model from './model.vue'
|
||||
const BACKEND_URL = getBackendUrl()
|
||||
const router = useRouter()
|
||||
const authStore = useAuthStore()
|
||||
|
||||
@ -73,15 +73,15 @@
|
||||
<el-button v-if="selectedPatient" type="primary"
|
||||
:class="selectedData.length == 2? 'patientprofile-selectedbutotn':'patientprofile-butotn'"
|
||||
@click="viewPatientProfile">
|
||||
<img v-if="selectedData.length == 2" src="@/assets/new/bi2.svg" alt="" style="margin-right: 8px;">
|
||||
<img v-else src="@/assets/new/bi.svg" alt="" style="margin-right: 8px;">
|
||||
<img v-if="selectedData.length == 2" src="@/assets/new/bi2.png" alt="" style="margin-right: 8px;">
|
||||
<img v-else src="@/assets/new/bi.png" alt="" style="margin-right: 8px;">
|
||||
报告对比
|
||||
</el-button>
|
||||
<el-button v-if="selectedPatient" type="primary"
|
||||
:class="selectedData.length>0? 'patientprofile-selectedbutotn':'patientprofile-butotn'"
|
||||
@click="viewPatientProfile">
|
||||
<img v-if="selectedData.length>0" src="@/assets/new/del2.svg" alt="" style="margin-right: 8px;">
|
||||
<img v-else src="@/assets/new/del.svg" alt="" style="margin-right: 8px;">
|
||||
<img v-if="selectedData.length>0" src="@/assets/new/del2.png" alt="" style="margin-right: 8px;">
|
||||
<img v-else src="@/assets/new/del.png" alt="" style="margin-right: 8px;">
|
||||
删除记录
|
||||
</el-button>
|
||||
</div>
|
||||
|
||||
609
frontend/src/renderer/src/views/model.vue
Normal file
609
frontend/src/renderer/src/views/model.vue
Normal file
@ -0,0 +1,609 @@
|
||||
<template>
|
||||
<div id="containermodel" >
|
||||
<div @click="asd" style="position: absolute;top: 0;">开始动画</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import { ref, reactive, computed, onMounted } from 'vue'
|
||||
|
||||
import * as THREE from 'three';
|
||||
import { GLTFLoader } from 'three/examples/jsm/loaders/GLTFLoader.js';
|
||||
|
||||
// const { ipcRenderer } = require('electron');
|
||||
|
||||
// Three.js场景设置
|
||||
let scene, camera, renderer, model;
|
||||
let targetQuaternion = new THREE.Quaternion();
|
||||
let currentQuaternion = new THREE.Quaternion();
|
||||
|
||||
// 调试控制参数
|
||||
let axisMapping = {
|
||||
roll: 'z', // 默认Roll对应Z轴
|
||||
pitch: 'x', // 默认Pitch对应X轴
|
||||
yaw: 'y' // 默认Yaw对应Y轴
|
||||
};
|
||||
|
||||
let manualOffsets = {
|
||||
x: 0,
|
||||
y: 0,
|
||||
z: 0
|
||||
};
|
||||
onMounted(() => {
|
||||
initThreeJS()
|
||||
// initDebugControls();
|
||||
})
|
||||
function initThreeJS() {
|
||||
// 创建场景
|
||||
scene = new THREE.Scene();
|
||||
scene.background = new THREE.Color(0x282828); // 稍微提亮背景
|
||||
let containermodel = document.getElementById('containermodel');
|
||||
// 创建相机
|
||||
camera = new THREE.PerspectiveCamera(75, containermodel.offsetWidth / containermodel.offsetHeight, 0.1, 1000);
|
||||
camera.position.set(3, 3, 2);
|
||||
camera.lookAt(0, 0, 0);
|
||||
|
||||
// 创建渲染器
|
||||
renderer = new THREE.WebGLRenderer({ antialias: true });
|
||||
renderer.setSize(containermodel.offsetWidth, containermodel.offsetHeight);
|
||||
renderer.shadowMap.enabled = true;
|
||||
renderer.shadowMap.type = THREE.PCFSoftShadowMap;
|
||||
renderer.outputColorSpace = THREE.SRGBColorSpace; // 改善颜色输出
|
||||
renderer.toneMapping = THREE.ACESFilmicToneMapping; // 添加色调映射
|
||||
renderer.toneMappingExposure = 1.2; // 增加曝光
|
||||
document.getElementById('containermodel').appendChild(renderer.domElement);
|
||||
|
||||
// 高强度照明系统
|
||||
// 环境光 - 大幅提升整体亮度
|
||||
const ambientLight = new THREE.AmbientLight(0x606060, 2.0);
|
||||
scene.add(ambientLight);
|
||||
|
||||
// 主方向光 - 从右上角照射,高强度
|
||||
const directionalLight1 = new THREE.DirectionalLight(0xffffff, 1.5);
|
||||
directionalLight1.position.set(10, 10, 5);
|
||||
directionalLight1.castShadow = true;
|
||||
directionalLight1.shadow.mapSize.width = 2048;
|
||||
directionalLight1.shadow.mapSize.height = 2048;
|
||||
directionalLight1.shadow.camera.near = 0.1;
|
||||
directionalLight1.shadow.camera.far = 50;
|
||||
directionalLight1.shadow.camera.left = -10;
|
||||
directionalLight1.shadow.camera.right = 10;
|
||||
directionalLight1.shadow.camera.top = 10;
|
||||
directionalLight1.shadow.camera.bottom = -10;
|
||||
scene.add(directionalLight1);
|
||||
|
||||
// 左侧补光 - 更强的强度
|
||||
const directionalLight2 = new THREE.DirectionalLight(0xbbddff, 1.0);
|
||||
directionalLight2.position.set(-8, 6, 4);
|
||||
scene.add(directionalLight2);
|
||||
|
||||
// 右侧补光
|
||||
const directionalLight3 = new THREE.DirectionalLight(0xffddbb, 0.8);
|
||||
directionalLight3.position.set(8, 6, -4);
|
||||
scene.add(directionalLight3);
|
||||
|
||||
// 顶部强光
|
||||
const topLight = new THREE.DirectionalLight(0xffffff, 1.0);
|
||||
topLight.position.set(0, 15, 0);
|
||||
scene.add(topLight);
|
||||
|
||||
// 底部补光(避免底部过暗)
|
||||
const bottomLight = new THREE.DirectionalLight(0x8899bb, 0.5);
|
||||
bottomLight.position.set(0, -5, 0);
|
||||
scene.add(bottomLight);
|
||||
|
||||
// 添加坐标系轴线
|
||||
createCoordinateAxes();
|
||||
|
||||
// 加载3D模型
|
||||
loadModel();
|
||||
|
||||
// 开始渲染循环
|
||||
animate();
|
||||
}
|
||||
|
||||
function loadModel() {
|
||||
const loader = new GLTFLoader();
|
||||
|
||||
// 尝试加载Model.glb文件
|
||||
loader.load('Model.glb',
|
||||
(gltf) => {
|
||||
model = gltf.scene;
|
||||
// 调整模型大小和位置
|
||||
const box = new THREE.Box3().setFromObject(model);
|
||||
const size = box.getSize(new THREE.Vector3());
|
||||
const maxDim = Math.max(size.x, size.y, size.z);
|
||||
const scale = 2 / maxDim; // 调整为合适大小
|
||||
model.scale.set(scale, scale, scale);
|
||||
|
||||
// 将模型置于中心
|
||||
const center = box.getCenter(new THREE.Vector3());
|
||||
model.position.set(-center.x * scale, -center.y * scale + 1, -center.z * scale);
|
||||
|
||||
// 启用阴影
|
||||
model.traverse((child) => {
|
||||
if (child.isMesh) {
|
||||
child.castShadow = true;
|
||||
child.receiveShadow = true;
|
||||
}
|
||||
});
|
||||
|
||||
scene.add(model);
|
||||
console.log('3D模型加载成功');
|
||||
},
|
||||
(progress) => {
|
||||
console.log('加载进度:', (progress.loaded / progress.total * 100) + '%');
|
||||
},
|
||||
(error) => {
|
||||
console.error('模型加载失败:', error);
|
||||
// 如果模型加载失败,创建一个替代的立方体
|
||||
createFallbackModel();
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
function createFallbackModel() {
|
||||
console.log('使用默认立方体模型');
|
||||
const geometry = new THREE.BoxGeometry(2, 2, 2);
|
||||
const material = new THREE.MeshLambertMaterial({
|
||||
color: 0x00ff00,
|
||||
transparent: true,
|
||||
opacity: 0.8
|
||||
});
|
||||
model = new THREE.Mesh(geometry, material);
|
||||
model.position.set(0, 1, 0);
|
||||
model.castShadow = true;
|
||||
model.receiveShadow = true;
|
||||
scene.add(model);
|
||||
}
|
||||
|
||||
function animate() {
|
||||
requestAnimationFrame(animate);
|
||||
|
||||
// 应用SLERP平滑插值到模型
|
||||
if (model) {
|
||||
currentQuaternion.slerp(targetQuaternion, 0.1);
|
||||
model.quaternion.copy(currentQuaternion);
|
||||
}
|
||||
|
||||
renderer.render(scene, camera);
|
||||
}
|
||||
|
||||
// 窗口大小调整
|
||||
window.addEventListener('resize', () => {
|
||||
let containermodel = document.getElementById('containermodel');
|
||||
|
||||
camera.aspect = containermodel.offsetWidth / containermodel.offsetHeight;
|
||||
camera.updateProjectionMatrix();
|
||||
renderer.setSize(containermodel.offsetWidth, containermodel.offsetHeight);
|
||||
});
|
||||
function asd(){
|
||||
setInterval(()=>{
|
||||
targetQuaternion.set(
|
||||
Math.random(),
|
||||
Math.random(),
|
||||
Math.random(),
|
||||
Math.random()
|
||||
);
|
||||
},500)
|
||||
|
||||
}
|
||||
// // IPC通信处理
|
||||
// ipcRenderer.on('orientation-data', (event, data) => {
|
||||
// if (data.quaternion && model) {
|
||||
// // 显示原始四元数
|
||||
// updateDebugDisplay('raw-quaternion', data.quaternion);
|
||||
|
||||
// // 应用轴向映射
|
||||
// const mappedQuaternion = applyAxisMapping(data.quaternion);
|
||||
// updateDebugDisplay('mapped-quaternion', mappedQuaternion);
|
||||
|
||||
// // 应用手动偏移
|
||||
// const finalQuaternion = applyManualOffsets(mappedQuaternion);
|
||||
// updateDebugDisplay('final-quaternion', finalQuaternion);
|
||||
|
||||
// // 更新目标四元数
|
||||
// targetQuaternion.set(
|
||||
// finalQuaternion.x,
|
||||
// finalQuaternion.y,
|
||||
// finalQuaternion.z,
|
||||
// finalQuaternion.w
|
||||
// );
|
||||
// }
|
||||
// });
|
||||
|
||||
// ipcRenderer.on('device-connected', () => {
|
||||
// updateConnectionStatus('已连接', true);
|
||||
// });
|
||||
|
||||
// ipcRenderer.on('device-disconnected', () => {
|
||||
// updateConnectionStatus('已断开', false);
|
||||
// });
|
||||
|
||||
// ipcRenderer.on('server-info', (event, info) => {
|
||||
// updateServerInfo(info);
|
||||
// });
|
||||
|
||||
function updateConnectionStatus(status, connected) {
|
||||
const statusElement = document.getElementById('connection-status');
|
||||
statusElement.textContent = `设备状态: ${status}`;
|
||||
statusElement.className = connected ? 'status connected' : 'status disconnected';
|
||||
}
|
||||
|
||||
function updateServerInfo(info) {
|
||||
const serverInfoElement = document.getElementById('server-info');
|
||||
|
||||
serverInfoElement.innerHTML = `
|
||||
服务器: ${info.ip}:${info.port}<br>
|
||||
<strong>手机访问:</strong><br>
|
||||
<a href="${info.mobileUrl}" target="_blank" style="color: #4CAF50;">${info.mobileUrl}</a><br>
|
||||
<small>或: <a href="${info.alternativeUrl}" target="_blank" style="color: #4CAF50;">${info.alternativeUrl}</a></small>
|
||||
`;
|
||||
|
||||
// 生成二维码
|
||||
generateQRCode(info.mobileUrl);
|
||||
}
|
||||
|
||||
async function generateQRCode(url) {
|
||||
const canvas = document.getElementById('qr-canvas');
|
||||
|
||||
try {
|
||||
// 使用qrcode库生成真正的二维码
|
||||
const QRCode = require('qrcode');
|
||||
|
||||
// 二维码选项
|
||||
const options = {
|
||||
width: 120,
|
||||
height: 120,
|
||||
margin: 1,
|
||||
color: {
|
||||
dark: '#000000',
|
||||
light: '#FFFFFF'
|
||||
},
|
||||
errorCorrectionLevel: 'M'
|
||||
};
|
||||
|
||||
// 生成二维码到canvas
|
||||
await QRCode.toCanvas(canvas, url, options);
|
||||
console.log('二维码生成成功:', url);
|
||||
|
||||
} catch (error) {
|
||||
console.error('二维码生成失败:', error);
|
||||
// 备用方案:显示文字
|
||||
const ctx = canvas.getContext('2d');
|
||||
ctx.fillStyle = '#ffffff';
|
||||
ctx.fillRect(0, 0, 120, 120);
|
||||
ctx.fillStyle = '#000000';
|
||||
ctx.font = '12px Arial';
|
||||
ctx.textAlign = 'center';
|
||||
ctx.fillText('QR生成失败', 60, 50);
|
||||
ctx.font = '10px Arial';
|
||||
ctx.fillText('请手动输入URL', 60, 70);
|
||||
}
|
||||
}
|
||||
|
||||
// 轴向映射函数
|
||||
function applyAxisMapping(quaternion) {
|
||||
// 将四元数转换为欧拉角进行映射
|
||||
const euler = new THREE.Euler().setFromQuaternion(
|
||||
new THREE.Quaternion(quaternion.x, quaternion.y, quaternion.z, quaternion.w)
|
||||
);
|
||||
|
||||
// 映射轴向
|
||||
const mappedEuler = {
|
||||
x: getAxisValue(euler, axisMapping.pitch),
|
||||
y: getAxisValue(euler, axisMapping.yaw),
|
||||
z: getAxisValue(euler, axisMapping.roll)
|
||||
};
|
||||
|
||||
// 转换回四元数
|
||||
const resultQuaternion = new THREE.Quaternion().setFromEuler(
|
||||
new THREE.Euler(mappedEuler.x, mappedEuler.y, mappedEuler.z)
|
||||
);
|
||||
|
||||
return {
|
||||
w: resultQuaternion.w,
|
||||
x: resultQuaternion.x,
|
||||
y: resultQuaternion.y,
|
||||
z: resultQuaternion.z
|
||||
};
|
||||
}
|
||||
|
||||
function getAxisValue(euler, mapping) {
|
||||
const value = {
|
||||
'x': euler.x,
|
||||
'-x': -euler.x,
|
||||
'y': euler.y,
|
||||
'-y': -euler.y,
|
||||
'z': euler.z,
|
||||
'-z': -euler.z
|
||||
}[mapping];
|
||||
|
||||
return value || 0;
|
||||
}
|
||||
|
||||
// 应用手动偏移
|
||||
function applyManualOffsets(quaternion) {
|
||||
// 创建偏移四元数
|
||||
const offsetQuaternion = new THREE.Quaternion().setFromEuler(
|
||||
new THREE.Euler(
|
||||
manualOffsets.x * Math.PI / 180,
|
||||
manualOffsets.y * Math.PI / 180,
|
||||
manualOffsets.z * Math.PI / 180
|
||||
)
|
||||
);
|
||||
|
||||
// 应用偏移
|
||||
const originalQuaternion = new THREE.Quaternion(quaternion.x, quaternion.y, quaternion.z, quaternion.w);
|
||||
const resultQuaternion = offsetQuaternion.multiply(originalQuaternion);
|
||||
|
||||
return {
|
||||
w: resultQuaternion.w,
|
||||
x: resultQuaternion.x,
|
||||
y: resultQuaternion.y,
|
||||
z: resultQuaternion.z
|
||||
};
|
||||
}
|
||||
|
||||
// 更新调试显示
|
||||
function updateDebugDisplay(elementId, quaternion) {
|
||||
const element = document.getElementById(elementId);
|
||||
if (element) {
|
||||
element.textContent = `w:${quaternion.w.toFixed(3)}, x:${quaternion.x.toFixed(3)}, y:${quaternion.y.toFixed(3)}, z:${quaternion.z.toFixed(3)}`;
|
||||
}
|
||||
}
|
||||
|
||||
// 创建坐标系轴线
|
||||
function createCoordinateAxes() {
|
||||
const axesGroup = new THREE.Group();
|
||||
|
||||
// 轴线长度和粗细
|
||||
const axisLength = 3;
|
||||
const axisRadius = 0.02;
|
||||
|
||||
// X轴 - 亮红色,使用自发光材质
|
||||
const xGeometry = new THREE.CylinderGeometry(axisRadius, axisRadius, axisLength, 8);
|
||||
const xMaterial = new THREE.MeshBasicMaterial({
|
||||
color: 0xff6666,
|
||||
transparent: true,
|
||||
opacity: 0.9
|
||||
});
|
||||
const xAxis = new THREE.Mesh(xGeometry, xMaterial);
|
||||
xAxis.rotation.z = -Math.PI / 2;
|
||||
xAxis.position.x = axisLength / 2;
|
||||
axesGroup.add(xAxis);
|
||||
|
||||
// X轴箭头
|
||||
const xArrowGeometry = new THREE.ConeGeometry(axisRadius * 3, axisRadius * 8, 8);
|
||||
const xArrow = new THREE.Mesh(xArrowGeometry, xMaterial);
|
||||
xArrow.rotation.z = -Math.PI / 2;
|
||||
xArrow.position.x = axisLength + axisRadius * 4;
|
||||
axesGroup.add(xArrow);
|
||||
|
||||
// Y轴 - 亮绿色,使用自发光材质
|
||||
const yGeometry = new THREE.CylinderGeometry(axisRadius, axisRadius, axisLength, 8);
|
||||
const yMaterial = new THREE.MeshBasicMaterial({
|
||||
color: 0x66ff66,
|
||||
transparent: true,
|
||||
opacity: 0.9
|
||||
});
|
||||
const yAxis = new THREE.Mesh(yGeometry, yMaterial);
|
||||
yAxis.position.y = axisLength / 2;
|
||||
axesGroup.add(yAxis);
|
||||
|
||||
// Y轴箭头
|
||||
const yArrowGeometry = new THREE.ConeGeometry(axisRadius * 3, axisRadius * 8, 8);
|
||||
const yArrow = new THREE.Mesh(yArrowGeometry, yMaterial);
|
||||
yArrow.position.y = axisLength + axisRadius * 4;
|
||||
axesGroup.add(yArrow);
|
||||
|
||||
// Z轴 - 亮蓝色,使用自发光材质
|
||||
const zGeometry = new THREE.CylinderGeometry(axisRadius, axisRadius, axisLength, 8);
|
||||
const zMaterial = new THREE.MeshBasicMaterial({
|
||||
color: 0x6666ff,
|
||||
transparent: true,
|
||||
opacity: 0.9
|
||||
});
|
||||
const zAxis = new THREE.Mesh(zGeometry, zMaterial);
|
||||
zAxis.rotation.x = Math.PI / 2;
|
||||
zAxis.position.z = axisLength / 2;
|
||||
axesGroup.add(zAxis);
|
||||
|
||||
// Z轴箭头
|
||||
const zArrowGeometry = new THREE.ConeGeometry(axisRadius * 3, axisRadius * 8, 8);
|
||||
const zArrow = new THREE.Mesh(zArrowGeometry, zMaterial);
|
||||
zArrow.rotation.x = Math.PI / 2;
|
||||
zArrow.position.z = axisLength + axisRadius * 4;
|
||||
axesGroup.add(zArrow);
|
||||
|
||||
// 原点球 - 亮白色自发光
|
||||
const originGeometry = new THREE.SphereGeometry(axisRadius * 2, 16, 16);
|
||||
const originMaterial = new THREE.MeshBasicMaterial({
|
||||
color: 0xffffff,
|
||||
transparent: true,
|
||||
opacity: 0.8
|
||||
});
|
||||
const origin = new THREE.Mesh(originGeometry, originMaterial);
|
||||
axesGroup.add(origin);
|
||||
|
||||
scene.add(axesGroup);
|
||||
}
|
||||
|
||||
// 初始化调试控制
|
||||
function initDebugControls() {
|
||||
// 轴向映射下拉框事件
|
||||
document.getElementById('roll-mapping').addEventListener('change', (e) => {
|
||||
axisMapping.roll = e.target.value;
|
||||
});
|
||||
|
||||
document.getElementById('pitch-mapping').addEventListener('change', (e) => {
|
||||
axisMapping.pitch = e.target.value;
|
||||
});
|
||||
|
||||
document.getElementById('yaw-mapping').addEventListener('change', (e) => {
|
||||
axisMapping.yaw = e.target.value;
|
||||
});
|
||||
|
||||
// 手动偏移滑动条事件
|
||||
['x', 'y', 'z'].forEach(axis => {
|
||||
const slider = document.getElementById(`offset-${axis}`);
|
||||
const valueDisplay = document.getElementById(`offset-${axis}-value`);
|
||||
|
||||
slider.addEventListener('input', (e) => {
|
||||
const value = parseInt(e.target.value);
|
||||
manualOffsets[axis] = value;
|
||||
valueDisplay.textContent = `${value}°`;
|
||||
});
|
||||
});
|
||||
|
||||
// 重置偏移按钮
|
||||
document.getElementById('reset-offsets').addEventListener('click', () => {
|
||||
['x', 'y', 'z'].forEach(axis => {
|
||||
manualOffsets[axis] = 0;
|
||||
document.getElementById(`offset-${axis}`).value = 0;
|
||||
document.getElementById(`offset-${axis}-value`).textContent = '0°';
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
// 初始化
|
||||
document.addEventListener('DOMContentLoaded', () => {
|
||||
initThreeJS();
|
||||
initDebugControls();
|
||||
|
||||
// 请求服务器信息
|
||||
ipcRenderer.invoke('get-server-info').then(info => {
|
||||
updateServerInfo(info);
|
||||
});
|
||||
});
|
||||
|
||||
// 添加键盘控制(可选)
|
||||
document.addEventListener('keydown', (event) => {
|
||||
if (!model) return;
|
||||
|
||||
switch(event.key) {
|
||||
case 'r':
|
||||
case 'R':
|
||||
// 重置模型旋转
|
||||
targetQuaternion.set(0, 0, 0, 1);
|
||||
currentQuaternion.set(0, 0, 0, 1);
|
||||
break;
|
||||
}
|
||||
});
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
#containermodel {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
position: relative;
|
||||
}
|
||||
|
||||
#info {
|
||||
position: absolute;
|
||||
top: 10px;
|
||||
left: 10px;
|
||||
color: white;
|
||||
background: rgba(0,0,0,0.8);
|
||||
padding: 15px;
|
||||
border-radius: 8px;
|
||||
z-index: 100;
|
||||
font-size: 14px;
|
||||
line-height: 1.4;
|
||||
max-width: 350px;
|
||||
box-shadow: 0 4px 12px rgba(0,0,0,0.3);
|
||||
}
|
||||
|
||||
#qr-section {
|
||||
margin-top: 15px;
|
||||
padding-top: 15px;
|
||||
border-top: 1px solid rgba(255,255,255,0.2);
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
#qr-canvas {
|
||||
background: white;
|
||||
border-radius: 4px;
|
||||
margin-top: 8px;
|
||||
}
|
||||
|
||||
#debug-panel {
|
||||
position: absolute;
|
||||
top: 10px;
|
||||
right: 10px;
|
||||
color: white;
|
||||
background: rgba(0,0,0,0.8);
|
||||
padding: 15px;
|
||||
border-radius: 5px;
|
||||
z-index: 100;
|
||||
font-size: 12px;
|
||||
width: 280px;
|
||||
}
|
||||
|
||||
.debug-section {
|
||||
margin: 10px 0;
|
||||
padding: 8px;
|
||||
background: rgba(255,255,255,0.1);
|
||||
border-radius: 3px;
|
||||
}
|
||||
|
||||
.debug-section h4 {
|
||||
margin: 0 0 8px 0;
|
||||
color: #4CAF50;
|
||||
font-size: 13px;
|
||||
}
|
||||
|
||||
.control-row {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
margin: 5px 0;
|
||||
gap: 8px;
|
||||
}
|
||||
|
||||
.control-row label {
|
||||
min-width: 20px;
|
||||
font-size: 11px;
|
||||
}
|
||||
|
||||
.control-row select {
|
||||
background: rgba(255,255,255,0.2);
|
||||
color: white;
|
||||
border: 1px solid rgba(255,255,255,0.3);
|
||||
border-radius: 3px;
|
||||
padding: 2px 5px;
|
||||
font-size: 11px;
|
||||
}
|
||||
|
||||
.control-row input[type="range"] {
|
||||
flex: 1;
|
||||
margin: 0 5px;
|
||||
}
|
||||
|
||||
.control-row .value {
|
||||
min-width: 40px;
|
||||
font-size: 11px;
|
||||
text-align: right;
|
||||
}
|
||||
|
||||
.status {
|
||||
margin: 5px 0;
|
||||
}
|
||||
|
||||
.connected {
|
||||
color: #4CAF50;
|
||||
}
|
||||
|
||||
.disconnected {
|
||||
color: #f44336;
|
||||
}
|
||||
|
||||
#qr-code {
|
||||
position: absolute;
|
||||
top: 10px;
|
||||
right: 10px;
|
||||
background: white;
|
||||
padding: 10px;
|
||||
border-radius: 5px;
|
||||
z-index: 100;
|
||||
}
|
||||
</style>
|
||||
Loading…
Reference in New Issue
Block a user