<template>
|
<div ref="container" class="scene-container">
|
<!-- 参数输入面板 -->
|
<div class="control-panel">
|
<div class="input-group">
|
<input
|
v-model.number="cubeParams.width"
|
placeholder="宽度"
|
type="number"
|
step="0.1"
|
min="0.1"
|
/>
|
<input
|
v-model.number="cubeParams.height"
|
placeholder="高度"
|
type="number"
|
step="0.1"
|
min="0.1"
|
/>
|
<input
|
v-model.number="cubeParams.depth"
|
placeholder="深度"
|
type="number"
|
step="0.1"
|
min="0.1"
|
/>
|
</div>
|
<div class="input-group">
|
<input v-model.number="cubeParams.x" placeholder="X坐标" type="number" step="0.1" />
|
<input v-model.number="cubeParams.y" placeholder="Y坐标" type="number" step="0.1" />
|
<input v-model.number="cubeParams.z" placeholder="Z坐标" type="number" step="0.1" />
|
</div>
|
<button @click="createCube">添加立方体</button>
|
</div>
|
</div>
|
</template>
|
|
<script>
|
import { onMounted, onBeforeUnmount, ref } from "vue";
|
import * as THREE from "three";
|
import { OrbitControls } from "three/examples/jsm/controls/OrbitControls.js";
|
import { EffectComposer } from "three/examples/jsm/postprocessing/EffectComposer.js";
|
import { RenderPass } from "three/examples/jsm/postprocessing/RenderPass.js";
|
|
export default {
|
setup() {
|
const container = ref(null);
|
let animationFrameId = null;
|
let scene = null;
|
let camera = null;
|
let renderer = null;
|
let controls = null;
|
let composer = null;
|
let client = ref(null); // 客户端对象
|
|
// 立方体参数响应式对象
|
const cubeParams = ref({
|
width: 2800,
|
height: 20,
|
depth: 700,
|
x: 0,
|
y: 0,
|
z: 0
|
});
|
|
// 创建立方体方法
|
const createCube = () => {
|
// 添加边界提示
|
const warningDistance = 5000;
|
if (
|
Math.abs(cubeParams.value.x) > warningDistance ||
|
Math.abs(cubeParams.value.y) > warningDistance ||
|
Math.abs(cubeParams.value.z) > warningDistance
|
) {
|
console.warn("物体位置超出推荐可视范围,建议调整相机参数");
|
}
|
|
// 创建几何体
|
const geometry = new THREE.BoxGeometry(
|
cubeParams.value.width,
|
cubeParams.value.height,
|
cubeParams.value.depth
|
);
|
|
// 创建材质(科技感材质)
|
// color: 0x00ffff,
|
// specular: 0xffffff,
|
// shininess: 100,
|
// emissive: 0x004d61,
|
// wireframe: false
|
const material = new THREE.MeshPhongMaterial({
|
color: 0x8b4513, // 基础色:鞍棕色
|
specular: 0x332211, // 高光色:深棕
|
shininess: 30, // 材质光泽度
|
roughness: 0.6 // 表面粗糙度
|
});
|
|
// 创建网格对象
|
const cube = new THREE.Mesh(geometry, material);
|
cube.position.set(
|
cubeParams.value.x,
|
cubeParams.value.y,
|
cubeParams.value.z
|
);
|
|
// 添加发光边框
|
const edges = new THREE.EdgesGeometry(geometry);
|
const edgeMaterial = new THREE.LineBasicMaterial({
|
color: 0xffffff,
|
linewidth: 2
|
});
|
const line = new THREE.LineSegments(edges, edgeMaterial);
|
cube.add(line);
|
|
scene.add(cube);
|
//fitCameraToObject(); // 添加立方体后自动调整视角
|
};
|
|
const createCube2 = (x, y, z, length, width, height) => {
|
debugger;
|
// 添加边界提示
|
const warningDistance = 5000;
|
if (
|
Math.abs(x) > warningDistance ||
|
Math.abs(y) > warningDistance ||
|
Math.abs(z) > warningDistance
|
) {
|
console.warn("物体位置超出推荐可视范围,建议调整相机参数");
|
}
|
|
// 创建几何体
|
const geometry = new THREE.BoxGeometry(length, height, width);
|
|
// 创建材质(科技感材质)
|
// color: 0x00ffff,
|
// specular: 0xffffff,
|
// shininess: 100,
|
// emissive: 0x004d61,
|
// wireframe: false
|
const material = new THREE.MeshPhongMaterial({
|
color: 0x8b4513, // 基础色:鞍棕色
|
specular: 0x332211, // 高光色:深棕
|
shininess: 30, // 材质光泽度
|
roughness: 0.6 // 表面粗糙度
|
});
|
|
// 创建网格对象
|
const cube = new THREE.Mesh(geometry, material);
|
cube.position.set(x, z, y);
|
|
// 添加发光边框
|
const edges = new THREE.EdgesGeometry(geometry);
|
const edgeMaterial = new THREE.LineBasicMaterial({
|
color: 0xffffff,
|
linewidth: 2
|
});
|
const line = new THREE.LineSegments(edges, edgeMaterial);
|
cube.add(line);
|
|
scene.add(cube);
|
//fitCameraToObject(); // 添加立方体后自动调整视角
|
};
|
|
// 优化视角适配算法
|
const fitCameraToObject = () => {
|
const box = new THREE.Box3().setFromObject(scene);
|
const size = box.getSize(new THREE.Vector3());
|
const diagonal = Math.sqrt(size.x ** 2 + size.y ** 2 + size.z ** 2);
|
|
// 动态计算观察距离(调整为对角线长度的1.2倍)
|
const distance = diagonal * 1.2;
|
|
// 保持45度俯视角度
|
camera.position.set(
|
box.max.x + distance * 0.7,
|
box.max.y + distance * 0.7,
|
box.max.z + distance * 0.7
|
);
|
|
// 限制最小观察距离
|
if (diagonal < 100) distance = 10; // 小物体特写
|
|
controls.target.copy(box.getCenter());
|
controls.update();
|
};
|
|
const initScene = () => {
|
scene = new THREE.Scene();
|
camera = new THREE.PerspectiveCamera(
|
55,
|
container.value.clientWidth / container.value.clientHeight,
|
1,
|
50000
|
);
|
|
// 设置相机初始位置
|
camera.position.set(2000, 2000, 2000); // 初始观察点抬高
|
camera.lookAt(0, 0, 0);
|
|
renderer = new THREE.WebGLRenderer({
|
antialias: true,
|
preserveDrawingBuffer: true,
|
alpha: true
|
});
|
|
renderer.setSize(
|
container.value.clientWidth,
|
container.value.clientHeight
|
);
|
renderer.setPixelRatio(window.devicePixelRatio);
|
container.value.appendChild(renderer.domElement);
|
|
composer = new EffectComposer(renderer);
|
composer.addPass(new RenderPass(scene, camera));
|
|
// 光源配置
|
const ambientLight = new THREE.AmbientLight(0xffffff, 0.6);
|
const directionalLight = new THREE.DirectionalLight(0xffeeee, 1.2);
|
directionalLight.position.set(2, 5, 3).normalize();
|
scene.add(ambientLight, directionalLight);
|
|
controls = new OrbitControls(camera, renderer.domElement);
|
controls.enableDamping = true;
|
controls.dampingFactor = 0.05;
|
controls.minDistance = 100;
|
controls.maxDistance = 50000; // 增大最大缩放距离
|
|
// 添加坐标轴辅助器
|
const axesHelper = new THREE.AxesHelper(10000); // 增大到10000单位
|
axesHelper.material.linewidth = 3; // 加粗线条
|
scene.add(axesHelper);
|
};
|
|
const animate = () => {
|
requestAnimationFrame(animate);
|
controls.update();
|
composer.render();
|
};
|
|
const handleResize = () => {
|
camera.aspect =
|
container.value.clientWidth / container.value.clientHeight;
|
camera.updateProjectionMatrix();
|
renderer.setSize(
|
container.value.clientWidth,
|
container.value.clientHeight
|
);
|
};
|
|
const cleanResources = () => {
|
scene.traverse(obj => {
|
if (obj.geometry) obj.geometry.dispose();
|
if (obj.material) {
|
Object.values(obj.material).forEach(prop => {
|
prop?.dispose?.();
|
});
|
}
|
});
|
renderer.dispose();
|
const gl = renderer.domElement.getContext("webgl");
|
gl?.getExtension("WEBGL_lose_context")?.loseContext();
|
};
|
|
const createSocket = url => {
|
// 创建WebSocket连接
|
//"ws://127.0.0.1:9295/admin"
|
client = new WebSocket(url);
|
|
client.onopen = function() {
|
client.onmessage = handleMessage;
|
console.log("WebSocket 连接成功");
|
};
|
|
client.onclose = function() {
|
console.log("WebSocket 连接关闭");
|
setTimeout(createSocket, 10000);
|
};
|
|
client.onerror = function() {};
|
};
|
|
const handleMessage = event => {
|
const data = JSON.parse(event.data);
|
createCube2(
|
data.x,
|
data.y,
|
data.z,
|
data.length,
|
data.width,
|
data.height
|
);
|
console.log("收到消息:", event.data);
|
console.log("收到消息:", data);
|
};
|
|
onMounted(() => {
|
createSocket(window.webConfig.webSocketUrl);
|
// 创建WebSocket连接
|
initScene();
|
animate();
|
window.addEventListener("resize", handleResize);
|
window.addEventListener("keydown", e => {
|
if (e.key === "f") fitCameraToObject(); // F键复位视角
|
if (e.key === "m") camera.position.set(2000, 2000, 2000); // M键返回默认视角
|
});
|
});
|
|
onBeforeUnmount(() => {
|
window.removeEventListener("resize", handleResize);
|
cancelAnimationFrame(animationFrameId);
|
cleanResources();
|
container.value?.removeChild(renderer.domElement);
|
});
|
|
return { container, cubeParams, createCube };
|
}
|
};
|
</script>
|
|
<style scoped>
|
.scene-container {
|
width: 100vw;
|
height: 100vh;
|
position: fixed;
|
background: linear-gradient(45deg, #02001d, #1a1b38, #004d61);
|
}
|
|
.control-panel {
|
position: fixed;
|
bottom: 20px;
|
right: 20px;
|
background: rgba(0, 0, 0, 0.7);
|
padding: 15px;
|
border-radius: 8px;
|
z-index: 1000;
|
backdrop-filter: blur(5px);
|
}
|
|
.input-group {
|
margin: 10px 0;
|
display: flex;
|
gap: 8px;
|
flex-wrap: wrap;
|
}
|
|
input {
|
width: 80px;
|
padding: 6px;
|
background: #1a1b38;
|
border: 1px solid #00ffff;
|
color: white;
|
border-radius: 4px;
|
}
|
|
button {
|
width: 100%;
|
padding: 8px;
|
background: #004d61;
|
color: white;
|
border: none;
|
border-radius: 4px;
|
cursor: pointer;
|
transition: background 0.3s;
|
}
|
|
button:hover {
|
background: #00ffff;
|
color: #02001d;
|
}
|
|
canvas {
|
display: block;
|
mix-blend-mode: screen;
|
}
|
</style>
|