全部修改架构
This commit is contained in:
@@ -2,16 +2,8 @@
|
||||
<div class="page_container">
|
||||
<el-row type="flex" justify="space-between">
|
||||
<el-col :span="10"><button class="button_control" :disabled="disabled" @click="addPoint"><p>打点</p></button></el-col>
|
||||
<el-col :span="14">
|
||||
<el-row type="flex" justify="end">
|
||||
<el-button type="primary" icon="el-icon-zoom-in" size="mini" @click="zoomIn">放大</el-button>
|
||||
<el-button type="primary" icon="el-icon-zoom-out" size="mini" @click="zoomOut">缩小</el-button>
|
||||
</el-row>
|
||||
</el-col>
|
||||
</el-row>
|
||||
<div class="canvas-container">
|
||||
<canvas id="canvas" ref="canvas" width="1920" height="1080"></canvas>
|
||||
</div>
|
||||
<gl-map></gl-map>
|
||||
<el-row type="flex" justify="end">
|
||||
<button class="button_control" @click="$router.push('/index/home')"><p>放弃建图</p></button>
|
||||
<button class="button_control" style="margin-left: 10px" :disabled="disabled" @click="_stopMapping"><p>结束建图</p></button>
|
||||
@@ -41,8 +33,13 @@
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { startMapping, setStation, stopMapping, getLocalMaps, oneClickDeployment, abandonMapping } from '@config/getData.js'
|
||||
import GlMap from './gl-map1.vue'
|
||||
import { startMapping, setStation, stopMapping, getLocalMaps, oneClickDeployment, abandonMapping } from '../../config/getData.js'
|
||||
export default {
|
||||
name: 'ModuleBuilding',
|
||||
components: {
|
||||
GlMap
|
||||
},
|
||||
beforeRouteLeave (to, from, next) {
|
||||
if (this.needsConfirmation) {
|
||||
this.$confirm('是否放弃本次建图?', '提示', {
|
||||
@@ -305,15 +302,6 @@ export default {
|
||||
</script>
|
||||
|
||||
<style lang="stylus" scoped>
|
||||
.canvas-container
|
||||
height calc(100% - 1rem)
|
||||
margin .14rem 0
|
||||
background-color rgba(0, 19, 48, 70%)
|
||||
box-shadow inset 1px 1px 7px 2px #4d9bcd
|
||||
overflow hidden
|
||||
#canvas
|
||||
width 100%
|
||||
height 100%
|
||||
.message
|
||||
min-width 380px
|
||||
border 1px solid #e1f3d8
|
||||
|
||||
@@ -33,7 +33,7 @@
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { startMapping, setStation, stopMapping, getLocalMaps, oneClickDeployment } from '@config/getData.js'
|
||||
import { startMapping, setStation, stopMapping, getLocalMaps, oneClickDeployment } from '../../config/getData.js'
|
||||
export default {
|
||||
data () {
|
||||
return {
|
||||
|
||||
@@ -65,8 +65,9 @@
|
||||
|
||||
<script>
|
||||
import SaveChain from './save-chain.vue'
|
||||
import { queryStation, queryTaskChain, queryTaskChainDtl, sendTask, saveTask, cancelTask, deleteTaskChain, updateStation } from '@config/getData.js'
|
||||
import { queryStation, queryTaskChain, queryTaskChainDtl, sendTask, saveTask, cancelTask, deleteTaskChain, updateStation } from '../../config/getData.js'
|
||||
export default {
|
||||
name: 'ModuleDevice',
|
||||
components: {
|
||||
SaveChain
|
||||
},
|
||||
|
||||
404
src/pages/modules/gl-map1.vue
Normal file
404
src/pages/modules/gl-map1.vue
Normal file
@@ -0,0 +1,404 @@
|
||||
<template>
|
||||
<div class="point-cloud-map">
|
||||
<div ref="canvasContainer" class="canvas-container"></div>
|
||||
<!-- 加载状态指示器 -->
|
||||
<div v-if="isLoading" class="loading-indicator">
|
||||
<i class="fa fa-circle-o-notch fa-spin"></i> 加载中...
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import * as THREE from 'three'
|
||||
import { OrbitControls } from 'three/examples/jsm/controls/OrbitControls'
|
||||
import { mapGetters } from 'vuex'
|
||||
import { points } from '../../config/point.js'
|
||||
export default {
|
||||
/* eslint-disable */
|
||||
data () {
|
||||
return {
|
||||
// Three.js核心对象
|
||||
scene: null,
|
||||
camera: null,
|
||||
renderer: null,
|
||||
controls: null,
|
||||
// 点云相关
|
||||
pointCloudGeometry: null,
|
||||
pointCloudMaterial: null,
|
||||
pointCloudMesh: null,
|
||||
allPoints: [], // 存储所有累加的点云数据
|
||||
pointCount: 0,
|
||||
pointScale: 1.0,
|
||||
// 小车相关
|
||||
vehicleImage: require('../../images/new/agv.png'),
|
||||
carMesh: null,
|
||||
carTexture: null,
|
||||
// WebSocket相关
|
||||
socket: null,
|
||||
isLoading: true,
|
||||
// 动画与性能
|
||||
viewSize: 20,
|
||||
animationId: null,
|
||||
lastUpdateTime: 0,
|
||||
updateInterval: 100, // 限制更新频率,毫秒
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
...mapGetters(['carPosition']),
|
||||
},
|
||||
watch: {
|
||||
carPosition: {
|
||||
handler(newVal) {
|
||||
this.updateCarPosition(newVal)
|
||||
},
|
||||
deep: true
|
||||
}
|
||||
},
|
||||
mounted () {
|
||||
// 初始化Three.js场景
|
||||
this.initThreeJs();
|
||||
// 初始化控制器(拖动和缩放)
|
||||
this.initControls();
|
||||
// 初始化点云
|
||||
this.initPointCloud();
|
||||
// 初始化小车模型
|
||||
this.initCar();
|
||||
// 初始化WebSocket连接
|
||||
this.initWebSocket();
|
||||
// this.init()
|
||||
// 启动动画循环
|
||||
this.startAnimationLoop();
|
||||
// 监听窗口大小变化
|
||||
window.addEventListener('resize', this.handleResize);
|
||||
// 初始加载完成
|
||||
setTimeout(() => {
|
||||
this.isLoading = false;
|
||||
}, 1000);
|
||||
},
|
||||
beforeDestroy () {
|
||||
// 清理资源
|
||||
if (this.socket) {
|
||||
this.socket.close();
|
||||
}
|
||||
if (this.animationId) {
|
||||
cancelAnimationFrame(this.animationId);
|
||||
}
|
||||
window.removeEventListener('resize', this.handleResize);
|
||||
// 清理Three.js资源
|
||||
if (this.renderer) this.renderer.dispose();
|
||||
if (this.pointCloudGeometry) this.pointCloudGeometry.dispose();
|
||||
if (this.pointCloudMaterial) this.pointCloudMaterial.dispose();
|
||||
if (this.carTexture) this.carTexture.dispose();
|
||||
},
|
||||
methods: {
|
||||
/**
|
||||
* 初始化Three.js场景、相机和渲染器
|
||||
*/
|
||||
initThreeJs() {
|
||||
const container = this.$refs.canvasContainer;
|
||||
const aspect = container.clientWidth / container.clientHeight
|
||||
|
||||
// 创建场景
|
||||
this.scene = new THREE.Scene();
|
||||
|
||||
// 创建正交相机(适合2D场景)
|
||||
this.camera = new THREE.OrthographicCamera(
|
||||
-this.viewSize * aspect / 2, // left
|
||||
this.viewSize * aspect / 2, // right
|
||||
this.viewSize / 2, // top
|
||||
-this.viewSize / 2, // bottom
|
||||
0.1, // near
|
||||
1000 // far
|
||||
);
|
||||
this.camera.position.z = 100; // 保持在Z轴上方,不影响2D视图
|
||||
|
||||
// 创建渲染器 - 启用alpha通道实现透明背景
|
||||
this.renderer = new THREE.WebGLRenderer({
|
||||
antialias: true,
|
||||
alpha: true, // 关键:启用alpha通道
|
||||
transparent: true // 关键:允许透明
|
||||
});
|
||||
this.renderer.setSize(container.clientWidth, container.clientHeight);
|
||||
this.renderer.setPixelRatio(window.devicePixelRatio);
|
||||
// 可选:设置clearAlpha确保完全透明
|
||||
this.renderer.setClearAlpha(0);
|
||||
container.appendChild(this.renderer.domElement);
|
||||
},
|
||||
|
||||
/**
|
||||
* 初始化控制器,支持拖动和缩放
|
||||
*/
|
||||
initControls() {
|
||||
this.controls = new OrbitControls(this.camera, this.renderer.domElement);
|
||||
this.controls.enableRotate = true; // 禁用旋转,保持2D视图
|
||||
this.controls.enableZoom = true; // 启用缩放
|
||||
this.controls.enablePan = true; // 启用平移(拖动)
|
||||
this.controls.screenSpacePanning = true; // 2D平移模式
|
||||
this.controls.touchZoomSpeed = 0.5; // 降低触摸缩放速度
|
||||
this.controls.panSpeed = 0.5; // 降低平移速度
|
||||
|
||||
// 鼠标/触摸优化
|
||||
this.controls.mouseButtons = {
|
||||
LEFT: THREE.MOUSE.PAN, // 左键拖动
|
||||
MIDDLE: THREE.MOUSE.DOLLY // 中键缩放(可选)
|
||||
};
|
||||
|
||||
// 触摸支持
|
||||
this.controls.touchAction = 'none';
|
||||
this.controls.touchPan = true;
|
||||
this.controls.touchZoom = true;
|
||||
|
||||
// 添加错误处理逻辑
|
||||
this.controls.addEventListener('error', (event) => {
|
||||
console.error('OrbitControls error:', event);
|
||||
});
|
||||
},
|
||||
|
||||
/**
|
||||
* 初始化点云
|
||||
*/
|
||||
initPointCloud() {
|
||||
// 使用BufferGeometry提高大量点的渲染性能
|
||||
this.pointCloudGeometry = new THREE.BufferGeometry();
|
||||
|
||||
// 点材质设置
|
||||
this.pointCloudMaterial = new THREE.PointsMaterial({
|
||||
color: 0xFFFFFF,
|
||||
size: 1, // 初始值会被动态覆盖
|
||||
sizeAttenuation: true, // 关键!
|
||||
transparent: true,
|
||||
opacity: 0.8
|
||||
});
|
||||
|
||||
// 创建点云网格
|
||||
this.pointCloudMesh = new THREE.Points(
|
||||
this.pointCloudGeometry,
|
||||
this.pointCloudMaterial
|
||||
);
|
||||
this.scene.add(this.pointCloudMesh);
|
||||
// 初始化历史数据为空
|
||||
this.allPoints = [];
|
||||
},
|
||||
|
||||
/**
|
||||
* 初始化小车模型
|
||||
*/
|
||||
initCar() {
|
||||
// 加载小车图片作为精灵
|
||||
const loader = new THREE.TextureLoader();
|
||||
this.carTexture = loader.load(this.vehicleImage, (texture) => {
|
||||
const imageAspect = 109 / 109
|
||||
const spriteWidth = 2
|
||||
const spriteHeight = spriteWidth / imageAspect
|
||||
|
||||
// 创建平面几何体
|
||||
const geometry = new THREE.PlaneGeometry(spriteWidth, spriteHeight);
|
||||
|
||||
// 创建材质并应用纹理
|
||||
const material = new THREE.MeshBasicMaterial({
|
||||
map: texture,
|
||||
transparent: true,
|
||||
opacity: 0.9
|
||||
});
|
||||
|
||||
// 创建网格对象
|
||||
this.carMesh = new THREE.Mesh(geometry, material);
|
||||
|
||||
// 设置锚点为小车中心(以便旋转正确)
|
||||
this.carMesh.position.set(spriteWidth / 2, spriteHeight / 2, 0);
|
||||
|
||||
// 添加到场景中
|
||||
this.scene.add(this.carMesh);
|
||||
|
||||
// 初始位置角度更新
|
||||
if (this.carPosition) {
|
||||
this.updateCarPosition(this.carPosition);
|
||||
}
|
||||
}, undefined, (error) => {
|
||||
console.error('小车图片加载失败:', error);
|
||||
});
|
||||
},
|
||||
|
||||
/**
|
||||
* 更新小车位置和角度
|
||||
*/
|
||||
updateCarPosition(position) {
|
||||
if (this.carMesh && position) {
|
||||
this.carMesh.position.x = position.x * this.pointScale;
|
||||
this.carMesh.position.y = position.y * this.pointScale;
|
||||
// 转换角度为弧度并调整方向(Three.js使用弧度)
|
||||
this.carMesh.rotation.z = position.angle;
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* 初始化WebSocket连接
|
||||
*/
|
||||
initWebSocket() {
|
||||
const wsHost = process.env.VUE_APP_API_BASE_URL.replace(/^https?:\/\//, '')
|
||||
const sid = this.$store.getters.userInfo === 'true' ? 1 : 2
|
||||
this.socket = new WebSocket(`ws://${wsHost}/webSocket/PointCloudData/${sid}`);
|
||||
|
||||
this.socket.onopen = () => {
|
||||
console.log('WebSocket连接已建立');
|
||||
this.isLoading = false;
|
||||
};
|
||||
|
||||
this.socket.onmessage = (event) => {
|
||||
try {
|
||||
// 限制更新频率,优化性能
|
||||
const now = Date.now();
|
||||
if (now - this.lastUpdateTime < this.updateInterval) {
|
||||
return;
|
||||
}
|
||||
this.lastUpdateTime = now;
|
||||
|
||||
const pointData = JSON.parse(event.data);
|
||||
console.log(pointData.data)
|
||||
this.updatePointCloud(pointData.data);
|
||||
} catch (error) {
|
||||
console.error('解析点云数据失败:', error);
|
||||
}
|
||||
};
|
||||
|
||||
this.socket.onclose = (event) => {
|
||||
console.log('WebSocket连接已关闭,代码:', event.code);
|
||||
this.isLoading = true;
|
||||
// 自动重连
|
||||
setTimeout(() => this.initWebSocket(), 3000);
|
||||
};
|
||||
|
||||
this.socket.onerror = (error) => {
|
||||
console.error('WebSocket错误:', error);
|
||||
this.isLoading = true;
|
||||
};
|
||||
},
|
||||
|
||||
init () {
|
||||
const pointData = points.data
|
||||
this.updatePointCloud(pointData);
|
||||
},
|
||||
|
||||
/**
|
||||
* 更新点云数据,优化大量点的渲染性能
|
||||
*/
|
||||
updatePointCloud(points) {
|
||||
if (!Array.isArray(points) || points.length === 0) return;
|
||||
|
||||
// 用于跟踪已存在的点,使用"x,y"作为唯一标识
|
||||
const existingPoints = {};
|
||||
// 先将已有点添加到跟踪对象
|
||||
this.allPoints.forEach(point => {
|
||||
const key = `${point.x},${point.y}`;
|
||||
existingPoints[key] = true;
|
||||
});
|
||||
|
||||
// 过滤新点中的重复点
|
||||
const newUniquePoints = points.filter(point => {
|
||||
const key = `${point.x},${point.y}`;
|
||||
if (!existingPoints[key]) {
|
||||
existingPoints[key] = true;
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
});
|
||||
|
||||
this.allPoints = [...this.allPoints, ...newUniquePoints];
|
||||
this.pointCount = this.allPoints.length; // 更新总点数
|
||||
|
||||
// 重新创建缓冲区(因数据量变化,需重新分配内存)
|
||||
const pointPositions = new Float32Array(this.pointCount * 3); // x, y, z各占1位
|
||||
this.pointCloudGeometry.setAttribute(
|
||||
'position',
|
||||
new THREE.BufferAttribute(pointPositions, 3)
|
||||
);
|
||||
|
||||
// 获取位置缓冲区并更新数据
|
||||
const positionAttribute = this.pointCloudGeometry.getAttribute('position');
|
||||
const positions = positionAttribute.array;
|
||||
|
||||
// 填充数据(z始终为0)
|
||||
for (let i = 0; i < this.pointCount; i++) {
|
||||
const point = this.allPoints[i];
|
||||
// 假设点数据格式为 {x: number, y: number}
|
||||
positions[i * 3] = (point.x || 0) * this.pointScale;
|
||||
positions[i * 3 + 1] = (point.y || 0) * this.pointScale;
|
||||
positions[i * 3 + 2] = 0; // Z轴固定为0
|
||||
}
|
||||
|
||||
// 标记属性需要更新
|
||||
positionAttribute.needsUpdate = true;
|
||||
},
|
||||
|
||||
/**
|
||||
* 处理窗口大小变化
|
||||
*/
|
||||
handleResize() {
|
||||
const container = this.$refs.canvasContainer;
|
||||
const width = container.clientWidth;
|
||||
const height = container.clientHeight;
|
||||
|
||||
// 更新相机
|
||||
this.camera.left = width / -2;
|
||||
this.camera.right = width / 2;
|
||||
this.camera.top = height / 2;
|
||||
this.camera.bottom = height / -2;
|
||||
this.camera.updateProjectionMatrix();
|
||||
|
||||
// 更新渲染器
|
||||
this.renderer.setSize(width, height);
|
||||
},
|
||||
|
||||
/**
|
||||
* 启动动画循环
|
||||
*/
|
||||
startAnimationLoop() {
|
||||
const animate = () => {
|
||||
this.animationId = requestAnimationFrame(animate);
|
||||
|
||||
// 更新控制器(用于阻尼效果)
|
||||
if (this.controls) {
|
||||
this.controls.update();
|
||||
}
|
||||
|
||||
// 渲染场景
|
||||
this.renderer.render(this.scene, this.camera);
|
||||
};
|
||||
|
||||
animate();
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="stylus" scoped>
|
||||
.point-cloud-map
|
||||
position relative
|
||||
width 100%
|
||||
height calc(100% - 1rem)
|
||||
margin .14rem 0
|
||||
background-color rgba(4, 33, 58, 70%)
|
||||
box-shadow inset 1px 1px 7px 2px #4d9bcd
|
||||
overflow hidden
|
||||
.canvas-container {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
position: relative;
|
||||
z-index: 0;
|
||||
}
|
||||
.loading-indicator {
|
||||
position: absolute;
|
||||
top: 50%;
|
||||
left: 50%;
|
||||
transform: translate(-50%, -50%);
|
||||
background-color: rgba(255, 255, 255, 0.8);
|
||||
padding: .01rem .02rem;
|
||||
border-radius: 4px;
|
||||
box-shadow: 0 2px 10px rgba(0, 0, 0, 0.1);
|
||||
font-size: .12rem;
|
||||
color: #333;
|
||||
z-index: 100;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
}
|
||||
</style>
|
||||
364
src/pages/modules/gl-map2.vue
Normal file
364
src/pages/modules/gl-map2.vue
Normal file
@@ -0,0 +1,364 @@
|
||||
<template>
|
||||
<div class="canvas-container" :style="{'background-color': isConnected ? 'rgba(4, 33, 58, 70%)' : '#fff'}">
|
||||
<div ref="container" class="point-cloud-container"></div>
|
||||
<!-- <div v-if="!isConnected" class="reload_bg"><el-button type="danger">刷新</el-button></div> -->
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import * as THREE from 'three'
|
||||
import { OrbitControls } from 'three/examples/jsm/controls/OrbitControls'
|
||||
import { mapGetters } from 'vuex'
|
||||
import { points } from '../../config/point.js'
|
||||
export default {
|
||||
/* eslint-disable */
|
||||
data () {
|
||||
return {
|
||||
isConnected: true,
|
||||
points: [], // 存储所有点云数据
|
||||
ws: null, // WebSocket 实例
|
||||
// Three.js 相关对象
|
||||
scene: null,
|
||||
camera: null,
|
||||
renderer: null,
|
||||
controls: null,
|
||||
pointCloud: null,
|
||||
vehicleSprite: null,
|
||||
// 渲染循环
|
||||
animationFrameId: null,
|
||||
// 性能优化
|
||||
pointsBuffer: [],
|
||||
bufferUpdateThreshold: 500,
|
||||
lastUpdateTime: 0,
|
||||
// 小车图片
|
||||
vehicleImage: require('../../images/new/agv.png'),
|
||||
// 2D视图参数
|
||||
viewSize: 20,
|
||||
minZoom: 0.5,
|
||||
maxZoom: 5
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
...mapGetters(['agvObj', 'position', 'rotation']),
|
||||
},
|
||||
watch: {
|
||||
agvObj: {
|
||||
handler() {
|
||||
this.updateVehiclePosition()
|
||||
},
|
||||
deep: true
|
||||
},
|
||||
position: {
|
||||
handler() {
|
||||
this.updateVehiclePosition()
|
||||
},
|
||||
deep: true
|
||||
},
|
||||
rotation: {
|
||||
handler() {
|
||||
this.updateVehiclePosition()
|
||||
},
|
||||
deep: true
|
||||
}
|
||||
},
|
||||
mounted () {
|
||||
this.initThreeJS()
|
||||
// this.initWebSocket()
|
||||
this.init()
|
||||
this.startRendering()
|
||||
window.addEventListener('resize', this.handleResize)
|
||||
},
|
||||
beforeDestroy () {
|
||||
if (this.ws) this.ws.close()
|
||||
window.removeEventListener('resize', this.handleResize)
|
||||
if (this.renderer) this.renderer.dispose()
|
||||
if (this.controls) {
|
||||
this.controls.dispose()
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
initWebSocket() {
|
||||
const wsHost = process.env.VUE_APP_API_BASE_URL.replace(/^https?:\/\//, '')
|
||||
const sid = this.$store.getters.userInfo === 'true' ? 1 : 2
|
||||
this.ws = new WebSocket(`ws://${wsHost}/webSocket/PointCloudData/${sid}`)
|
||||
|
||||
this.ws.onopen = () => {
|
||||
console.log('WebSocket connected')
|
||||
}
|
||||
|
||||
this.ws.onmessage = (event) => {
|
||||
const newPoints = event.data
|
||||
// 确保所有点都在XY平面(z=0)
|
||||
const points2D = newPoints.map(p => ({ x: p.x, y: p.y, z: 0 }))
|
||||
this.processNewPoints(points2D)
|
||||
}
|
||||
|
||||
this.ws.onerror = (error) => {
|
||||
console.error('WebSocket error:', error)
|
||||
}
|
||||
|
||||
this.ws.onclose = () => {
|
||||
console.log('WebSocket disconnected')
|
||||
}
|
||||
},
|
||||
|
||||
init () {
|
||||
this.isConnected = true
|
||||
const newPoints = points.data
|
||||
const points2D = newPoints.map(p => ({ x: p.x, y: p.y, z: 0 }))
|
||||
this.processNewPoints(points2D)
|
||||
},
|
||||
|
||||
initThreeJS() {
|
||||
// viewSize: 控制视图范围大小,值越小看到的范围越小(视角越近)
|
||||
// camera.position.z: 相机Z轴位置,值越小视角越近
|
||||
// camera.zoom: 直接控制缩放级别,大于1放大,小于1缩小
|
||||
// 1. 创建场景
|
||||
this.scene = new THREE.Scene()
|
||||
|
||||
// 2. 创建正交相机(2D视图)
|
||||
const container = this.$refs.container
|
||||
const aspect = container.clientWidth / container.clientHeight
|
||||
|
||||
this.camera = new THREE.OrthographicCamera(
|
||||
-this.viewSize * aspect / 2,
|
||||
this.viewSize * aspect / 2,
|
||||
this.viewSize / 2,
|
||||
-this.viewSize / 2,
|
||||
0.1,
|
||||
1000
|
||||
)
|
||||
this.camera.position.set(0, 0, 40)
|
||||
this.camera.lookAt(0, 0, 0)
|
||||
|
||||
// 3.创建渲染器 - 启用alpha通道实现透明背景
|
||||
this.renderer = new THREE.WebGLRenderer({
|
||||
antialias: true,
|
||||
alpha: true, // 关键:启用alpha通道
|
||||
transparent: true // 关键:允许透明
|
||||
});
|
||||
this.renderer.setPixelRatio(window.devicePixelRatio)
|
||||
this.renderer.setSize(container.clientWidth, container.clientHeight)
|
||||
// 可选:设置clearAlpha确保完全透明
|
||||
this.renderer.setClearAlpha(0);
|
||||
container.appendChild(this.renderer.domElement)
|
||||
|
||||
// 4. 添加OrbitControls并配置为2D模式
|
||||
this.controls = new OrbitControls(this.camera, this.renderer.domElement)
|
||||
this.configure2DControls()
|
||||
|
||||
// 5. 添加2D坐标轴辅助
|
||||
const axesHelper = new THREE.AxesHelper(10)
|
||||
this.scene.add(axesHelper)
|
||||
|
||||
// 6. 初始化点云
|
||||
this.initPointCloud()
|
||||
|
||||
// 7. 初始化车辆精灵
|
||||
this.initVehicleSprite()
|
||||
},
|
||||
|
||||
configure2DControls() {
|
||||
// 禁用旋转
|
||||
this.controls.enableRotate = false
|
||||
|
||||
// 启用平面平移
|
||||
this.controls.screenSpacePanning = true
|
||||
|
||||
// 设置缩放限制
|
||||
this.controls.minZoom = this.minZoom
|
||||
this.controls.maxZoom = this.maxZoom
|
||||
|
||||
// 启用阻尼效果
|
||||
this.controls.enableDamping = true
|
||||
this.controls.dampingFactor = 0.25
|
||||
|
||||
// 确保相机保持2D视角
|
||||
this.controls.addEventListener('change', () => {
|
||||
this.camera.position.z = 40
|
||||
// this.camera.lookAt(this.controls.target)
|
||||
})
|
||||
|
||||
// 修复触摸事件处理
|
||||
this.fixTouchEvents()
|
||||
},
|
||||
|
||||
fixTouchEvents() {
|
||||
// 确保控制器的触摸处理函数存在
|
||||
if (!this.controls) return;
|
||||
|
||||
// 备份原始触摸处理函数
|
||||
const originalOnTouchStart = this.controls.onTouchStart
|
||||
const originalOnTouchMove = this.controls.onTouchMove
|
||||
const originalOnTouchEnd = this.controls.onTouchEnd
|
||||
|
||||
// 修复触摸开始事件
|
||||
this.controls.onTouchStart = (event) => {
|
||||
if (!event.touches) return
|
||||
if (originalOnTouchStart) originalOnTouchStart(event)
|
||||
}
|
||||
|
||||
// 修复触摸移动事件
|
||||
this.controls.onTouchMove = (event) => {
|
||||
if (!event.touches || event.touches.length === 0) return
|
||||
if (event.touches[0] && event.touches[0].clientX !== undefined) {
|
||||
if (originalOnTouchMove) originalOnTouchMove(event)
|
||||
}
|
||||
}
|
||||
|
||||
// 修复触摸结束事件
|
||||
this.controls.onTouchEnd = (event) => {
|
||||
if (!event.touches) return
|
||||
if (originalOnTouchEnd) originalOnTouchEnd(event)
|
||||
}
|
||||
},
|
||||
|
||||
initPointCloud() {
|
||||
const geometry = new THREE.BufferGeometry()
|
||||
const material = new THREE.PointsMaterial({
|
||||
color: 0xffffff,
|
||||
size: 1,
|
||||
transparent: true,
|
||||
opacity: 1
|
||||
})
|
||||
|
||||
this.pointCloud = new THREE.Points(geometry, material)
|
||||
this.scene.add(this.pointCloud)
|
||||
},
|
||||
|
||||
initVehicleSprite() {
|
||||
const textureLoader = new THREE.TextureLoader()
|
||||
textureLoader.load(this.vehicleImage, (texture) => {
|
||||
// 97px × 67px图片的比例
|
||||
const imageAspect = 97 / 67
|
||||
const spriteWidth = 2
|
||||
const spriteHeight = spriteWidth / imageAspect
|
||||
|
||||
this.vehicleSprite = new THREE.Sprite(
|
||||
new THREE.SpriteMaterial({
|
||||
map: texture,
|
||||
transparent: true,
|
||||
opacity: 0.9
|
||||
})
|
||||
)
|
||||
this.vehicleSprite.scale.set(spriteWidth, spriteHeight, 1)
|
||||
|
||||
// 角度偏移(根据图片初始朝向调整)
|
||||
this.updateVehiclePosition()
|
||||
this.scene.add(this.vehicleSprite)
|
||||
})
|
||||
},
|
||||
|
||||
processNewPoints(newPoints) {
|
||||
this.pointsBuffer.push(...newPoints)
|
||||
const now = performance.now()
|
||||
if (this.pointsBuffer.length >= this.bufferUpdateThreshold || now - this.lastUpdateTime > 1000) {
|
||||
this.updatePointCloudGeometry()
|
||||
this.lastUpdateTime = now
|
||||
}
|
||||
},
|
||||
|
||||
updatePointCloudGeometry() {
|
||||
if (this.pointsBuffer.length === 0) return
|
||||
|
||||
this.points = this.points.concat(this.pointsBuffer)
|
||||
this.pointsBuffer = []
|
||||
|
||||
if (this.points.length > 50000) {
|
||||
this.points = this.points.slice(-40000)
|
||||
}
|
||||
|
||||
const positions = new Float32Array(this.points.length * 3)
|
||||
for (let i = 0; i < this.points.length; i++) {
|
||||
positions[i * 3] = this.points[i].x
|
||||
positions[i * 3 + 1] = this.points[i].y
|
||||
positions[i * 3 + 2] = 0
|
||||
}
|
||||
|
||||
this.pointCloud.geometry.dispose()
|
||||
this.pointCloud.geometry = new THREE.BufferGeometry()
|
||||
this.pointCloud.geometry.setAttribute('position', new THREE.BufferAttribute(positions, 3))
|
||||
},
|
||||
|
||||
updateVehiclePosition() {
|
||||
if (!this.vehicleSprite) return
|
||||
|
||||
this.vehicleSprite.position.set(
|
||||
this.position.x,
|
||||
this.position.y,
|
||||
0
|
||||
)
|
||||
const radians = this.rotation * (Math.PI / 180)
|
||||
|
||||
this.vehicleSprite.rotation.X = radians
|
||||
},
|
||||
|
||||
startRendering() {
|
||||
const render = () => {
|
||||
this.controls.update()
|
||||
this.renderer.render(this.scene, this.camera)
|
||||
this.animationFrameId = requestAnimationFrame(render)
|
||||
}
|
||||
render()
|
||||
},
|
||||
|
||||
handleResize() {
|
||||
const container = this.$refs.container
|
||||
const width = container.clientWidth
|
||||
const height = container.clientHeight
|
||||
const aspect = width / height
|
||||
|
||||
this.camera.left = -this.viewSize * aspect / 2
|
||||
this.camera.right = this.viewSize * aspect / 2
|
||||
this.camera.top = this.viewSize / 2
|
||||
this.camera.bottom = -this.viewSize / 2
|
||||
this.camera.updateProjectionMatrix()
|
||||
|
||||
this.renderer.setSize(width, height)
|
||||
},
|
||||
|
||||
zoomToArea(minX, maxX, minY, maxY) {
|
||||
const width = maxX - minX
|
||||
const height = maxY - minY
|
||||
const centerX = (minX + maxX) / 2
|
||||
const centerY = (minY + maxY) / 2
|
||||
|
||||
const margin = 0.1
|
||||
const targetViewSize = Math.max(width, height) * (1 + margin)
|
||||
|
||||
// 计算合适的缩放级别
|
||||
const zoomLevel = this.viewSize / targetViewSize
|
||||
this.controls.target.set(centerX, centerY, 0)
|
||||
// 通过调整camera.zoom实现平滑缩放
|
||||
this.camera.zoom = Math.min(this.maxZoom, Math.max(this.minZoom, zoomLevel))
|
||||
this.camera.updateProjectionMatrix()
|
||||
this.controls.update()
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="stylus" scoped>
|
||||
.canvas-container
|
||||
position relative
|
||||
display flex
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
height calc(100% - 1rem)
|
||||
margin .14rem 0
|
||||
box-shadow inset 1px 1px 7px 2px #4d9bcd
|
||||
overflow hidden
|
||||
.point-cloud-container
|
||||
width 100%
|
||||
height 100%
|
||||
.reload_bg {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
position: absolute;
|
||||
top: 0;
|
||||
left: 0;
|
||||
}
|
||||
</style>
|
||||
384
src/pages/modules/gl-map的副本.vue
Normal file
384
src/pages/modules/gl-map的副本.vue
Normal file
@@ -0,0 +1,384 @@
|
||||
<template>
|
||||
<div class="point-cloud-map">
|
||||
<div ref="canvasContainer" class="canvas-container"></div>
|
||||
<!-- 加载状态指示器 -->
|
||||
<div v-if="isLoading" class="loading-indicator">
|
||||
<i class="fa fa-circle-o-notch fa-spin"></i> 加载中...
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import * as THREE from 'three'
|
||||
import { OrbitControls } from 'three/examples/jsm/controls/OrbitControls'
|
||||
import { mapGetters } from 'vuex'
|
||||
import { points } from '../../config/point.js'
|
||||
export default {
|
||||
/* eslint-disable */
|
||||
data () {
|
||||
return {
|
||||
// Three.js核心对象
|
||||
scene: null,
|
||||
camera: null,
|
||||
renderer: null,
|
||||
controls: null,
|
||||
// 点云相关
|
||||
pointCloudGeometry: null,
|
||||
pointCloudMaterial: null,
|
||||
pointCloudMesh: null,
|
||||
pointCount: 0,
|
||||
pointScale: 1.0,
|
||||
// 小车相关
|
||||
vehicleImage: require('../../images/new/agv.png'),
|
||||
carMesh: null,
|
||||
carTexture: null,
|
||||
// WebSocket相关
|
||||
socket: null,
|
||||
isLoading: true,
|
||||
// 动画与性能
|
||||
viewSize: 20,
|
||||
animationId: null,
|
||||
lastUpdateTime: 0,
|
||||
updateInterval: 100, // 限制更新频率,毫秒
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
...mapGetters(['carPosition']),
|
||||
},
|
||||
watch: {
|
||||
carPosition: {
|
||||
handler(newVal) {
|
||||
this.updateCarPosition(newVal)
|
||||
},
|
||||
deep: true
|
||||
}
|
||||
},
|
||||
mounted () {
|
||||
// 初始化Three.js场景
|
||||
this.initThreeJs();
|
||||
// 初始化控制器(拖动和缩放)
|
||||
this.initControls();
|
||||
// 初始化点云
|
||||
this.initPointCloud();
|
||||
// 初始化小车模型
|
||||
this.initCar();
|
||||
// 初始化WebSocket连接
|
||||
// this.initWebSocket();
|
||||
this.init()
|
||||
// 启动动画循环
|
||||
this.startAnimationLoop();
|
||||
// 监听窗口大小变化
|
||||
window.addEventListener('resize', this.handleResize);
|
||||
// 初始加载完成
|
||||
setTimeout(() => {
|
||||
this.isLoading = false;
|
||||
}, 1000);
|
||||
},
|
||||
beforeDestroy () {
|
||||
// 清理资源
|
||||
if (this.socket) {
|
||||
this.socket.close();
|
||||
}
|
||||
if (this.animationId) {
|
||||
cancelAnimationFrame(this.animationId);
|
||||
}
|
||||
window.removeEventListener('resize', this.handleResize);
|
||||
// 清理Three.js资源
|
||||
if (this.renderer) this.renderer.dispose();
|
||||
if (this.pointCloudGeometry) this.pointCloudGeometry.dispose();
|
||||
if (this.pointCloudMaterial) this.pointCloudMaterial.dispose();
|
||||
if (this.carTexture) this.carTexture.dispose();
|
||||
},
|
||||
methods: {
|
||||
/**
|
||||
* 初始化Three.js场景、相机和渲染器
|
||||
*/
|
||||
initThreeJs() {
|
||||
const container = this.$refs.canvasContainer;
|
||||
const aspect = container.clientWidth / container.clientHeight
|
||||
|
||||
// 创建场景
|
||||
this.scene = new THREE.Scene();
|
||||
|
||||
// 创建正交相机(适合2D场景)
|
||||
this.camera = new THREE.OrthographicCamera(
|
||||
-this.viewSize * aspect / 2, // left
|
||||
this.viewSize * aspect / 2, // right
|
||||
this.viewSize / 2, // top
|
||||
-this.viewSize / 2, // bottom
|
||||
0.1, // near
|
||||
1000 // far
|
||||
);
|
||||
this.camera.position.z = 100; // 保持在Z轴上方,不影响2D视图
|
||||
|
||||
// 创建渲染器 - 启用alpha通道实现透明背景
|
||||
this.renderer = new THREE.WebGLRenderer({
|
||||
antialias: true,
|
||||
alpha: true, // 关键:启用alpha通道
|
||||
transparent: true // 关键:允许透明
|
||||
});
|
||||
this.renderer.setSize(container.clientWidth, container.clientHeight);
|
||||
this.renderer.setPixelRatio(window.devicePixelRatio);
|
||||
// 可选:设置clearAlpha确保完全透明
|
||||
this.renderer.setClearAlpha(0);
|
||||
container.appendChild(this.renderer.domElement);
|
||||
},
|
||||
|
||||
/**
|
||||
* 初始化控制器,支持拖动和缩放
|
||||
*/
|
||||
initControls() {
|
||||
this.controls = new OrbitControls(this.camera, this.renderer.domElement);
|
||||
this.controls.enableRotate = false; // 禁用旋转,保持2D视图
|
||||
this.controls.enableZoom = true; // 启用缩放
|
||||
this.controls.enablePan = true; // 启用平移(拖动)
|
||||
this.controls.screenSpacePanning = true; // 2D平移模式
|
||||
this.controls.touchZoomSpeed = 0.5; // 降低触摸缩放速度
|
||||
this.controls.panSpeed = 0.3; // 降低平移速度
|
||||
|
||||
// 鼠标/触摸优化
|
||||
this.controls.mouseButtons = {
|
||||
LEFT: THREE.MOUSE.PAN, // 左键拖动
|
||||
MIDDLE: THREE.MOUSE.DOLLY // 中键缩放(可选)
|
||||
};
|
||||
|
||||
// 触摸支持
|
||||
this.controls.touchAction = 'none';
|
||||
this.controls.touchPan = true;
|
||||
this.controls.touchZoom = true;
|
||||
|
||||
// 添加错误处理逻辑
|
||||
this.controls.addEventListener('error', (event) => {
|
||||
console.error('OrbitControls error:', event);
|
||||
});
|
||||
},
|
||||
|
||||
/**
|
||||
* 初始化点云
|
||||
*/
|
||||
initPointCloud() {
|
||||
// 使用BufferGeometry提高大量点的渲染性能
|
||||
this.pointCloudGeometry = new THREE.BufferGeometry();
|
||||
|
||||
// 点材质设置
|
||||
this.pointCloudMaterial = new THREE.PointsMaterial({
|
||||
color: 0xFFFFFF,
|
||||
size: 1, // 初始值会被动态覆盖
|
||||
sizeAttenuation: true, // 关键!
|
||||
transparent: true,
|
||||
opacity: 0.8
|
||||
});
|
||||
|
||||
// 创建点云网格
|
||||
this.pointCloudMesh = new THREE.Points(
|
||||
this.pointCloudGeometry,
|
||||
this.pointCloudMaterial
|
||||
);
|
||||
this.scene.add(this.pointCloudMesh);
|
||||
},
|
||||
|
||||
/**
|
||||
* 初始化小车模型
|
||||
*/
|
||||
initCar() {
|
||||
// 加载小车图片作为精灵
|
||||
const loader = new THREE.TextureLoader();
|
||||
this.carTexture = loader.load(this.vehicleImage, (texture) => {
|
||||
// 97px × 67px图片的比例
|
||||
const imageAspect = 97 / 67
|
||||
const spriteWidth = 2
|
||||
const spriteHeight = spriteWidth / imageAspect
|
||||
|
||||
// 创建平面几何体
|
||||
const geometry = new THREE.PlaneGeometry(spriteWidth, spriteHeight);
|
||||
|
||||
// 创建材质并应用纹理
|
||||
const material = new THREE.MeshBasicMaterial({
|
||||
map: texture,
|
||||
transparent: true,
|
||||
opacity: 0.9
|
||||
});
|
||||
|
||||
// 创建网格对象
|
||||
this.carMesh = new THREE.Mesh(geometry, material);
|
||||
|
||||
// 设置锚点为小车中心(以便旋转正确)
|
||||
this.carMesh.position.set(spriteWidth / 2, spriteHeight / 2, 0);
|
||||
|
||||
// 添加到场景中
|
||||
this.scene.add(this.carMesh);
|
||||
|
||||
// 初始位置角度更新
|
||||
if (this.carPosition) {
|
||||
this.updateCarPosition(this.carPosition);
|
||||
}
|
||||
}, undefined, (error) => {
|
||||
console.error('小车图片加载失败:', error);
|
||||
});
|
||||
},
|
||||
|
||||
/**
|
||||
* 更新小车位置和角度
|
||||
*/
|
||||
updateCarPosition(position) {
|
||||
if (this.carMesh && position) {
|
||||
this.carMesh.position.x = position.x * this.pointScale;
|
||||
this.carMesh.position.y = position.y * this.pointScale;
|
||||
// 转换角度为弧度并调整方向(Three.js使用弧度)
|
||||
this.carMesh.rotation.z = THREE.MathUtils.degToRad(position.angle);
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* 初始化WebSocket连接
|
||||
*/
|
||||
initWebSocket() {
|
||||
const wsHost = process.env.VUE_APP_API_BASE_URL.replace(/^https?:\/\//, '')
|
||||
const sid = this.$store.getters.userInfo === 'true' ? 1 : 2
|
||||
this.socket = new WebSocket(`ws://${wsHost}/webSocket/PointCloudData/${sid}`);
|
||||
|
||||
this.socket.onopen = () => {
|
||||
console.log('WebSocket连接已建立');
|
||||
this.isLoading = false;
|
||||
};
|
||||
|
||||
this.socket.onmessage = (event) => {
|
||||
try {
|
||||
// 限制更新频率,优化性能
|
||||
const now = Date.now();
|
||||
if (now - this.lastUpdateTime < this.updateInterval) {
|
||||
return;
|
||||
}
|
||||
this.lastUpdateTime = now;
|
||||
|
||||
const pointData = event.data;
|
||||
this.updatePointCloud(pointData);
|
||||
} catch (error) {
|
||||
console.error('解析点云数据失败:', error);
|
||||
}
|
||||
};
|
||||
|
||||
this.socket.onclose = (event) => {
|
||||
console.log('WebSocket连接已关闭,代码:', event.code);
|
||||
this.isLoading = true;
|
||||
// 自动重连
|
||||
setTimeout(() => this.initWebSocket(), 3000);
|
||||
};
|
||||
|
||||
this.socket.onerror = (error) => {
|
||||
console.error('WebSocket错误:', error);
|
||||
this.isLoading = true;
|
||||
};
|
||||
},
|
||||
|
||||
init () {
|
||||
const pointData = points.data
|
||||
this.updatePointCloud(pointData);
|
||||
},
|
||||
|
||||
/**
|
||||
* 更新点云数据,优化大量点的渲染性能
|
||||
*/
|
||||
updatePointCloud(points) {
|
||||
if (!Array.isArray(points) || points.length === 0) return;
|
||||
|
||||
// 性能优化:仅在点数变化时重新分配缓冲区
|
||||
if (points.length !== this.pointCount) {
|
||||
this.pointCount = points.length;
|
||||
// 创建新的缓冲区 (x, y, z),z始终为0
|
||||
const positions = new Float32Array(this.pointCount * 3);
|
||||
this.pointCloudGeometry.setAttribute(
|
||||
'position',
|
||||
new THREE.BufferAttribute(positions, 3)
|
||||
);
|
||||
}
|
||||
|
||||
// 获取位置缓冲区并更新数据
|
||||
const positionAttribute = this.pointCloudGeometry.getAttribute('position');
|
||||
const positions = positionAttribute.array;
|
||||
|
||||
// 填充数据(z始终为0)
|
||||
for (let i = 0; i < this.pointCount; i++) {
|
||||
const point = points[i];
|
||||
// 假设点数据格式为 {x: number, y: number}
|
||||
positions[i * 3] = (point.x || 0) * this.pointScale;
|
||||
positions[i * 3 + 1] = (point.y || 0) * this.pointScale;
|
||||
positions[i * 3 + 2] = 0; // Z轴固定为0
|
||||
}
|
||||
|
||||
// 标记属性需要更新
|
||||
positionAttribute.needsUpdate = true;
|
||||
},
|
||||
|
||||
/**
|
||||
* 处理窗口大小变化
|
||||
*/
|
||||
handleResize() {
|
||||
const container = this.$refs.canvasContainer;
|
||||
const width = container.clientWidth;
|
||||
const height = container.clientHeight;
|
||||
|
||||
// 更新相机
|
||||
this.camera.left = width / -2;
|
||||
this.camera.right = width / 2;
|
||||
this.camera.top = height / 2;
|
||||
this.camera.bottom = height / -2;
|
||||
this.camera.updateProjectionMatrix();
|
||||
|
||||
// 更新渲染器
|
||||
this.renderer.setSize(width, height);
|
||||
},
|
||||
|
||||
/**
|
||||
* 启动动画循环
|
||||
*/
|
||||
startAnimationLoop() {
|
||||
const animate = () => {
|
||||
this.animationId = requestAnimationFrame(animate);
|
||||
|
||||
// 更新控制器(用于阻尼效果)
|
||||
if (this.controls) {
|
||||
this.controls.update();
|
||||
}
|
||||
|
||||
// 渲染场景
|
||||
this.renderer.render(this.scene, this.camera);
|
||||
};
|
||||
|
||||
animate();
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="stylus" scoped>
|
||||
.point-cloud-map
|
||||
position relative
|
||||
width 100%
|
||||
height calc(100% - 1rem)
|
||||
margin .14rem 0
|
||||
background-color rgba(4, 33, 58, 70%)
|
||||
box-shadow inset 1px 1px 7px 2px #4d9bcd
|
||||
overflow hidden
|
||||
.canvas-container {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
position: relative;
|
||||
z-index: 0;
|
||||
}
|
||||
.loading-indicator {
|
||||
position: absolute;
|
||||
top: 50%;
|
||||
left: 50%;
|
||||
transform: translate(-50%, -50%);
|
||||
background-color: rgba(255, 255, 255, 0.8);
|
||||
padding: .01rem .02rem;
|
||||
border-radius: 4px;
|
||||
box-shadow: 0 2px 10px rgba(0, 0, 0, 0.1);
|
||||
font-size: .12rem;
|
||||
color: #333;
|
||||
z-index: 100;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
}
|
||||
</style>
|
||||
@@ -47,7 +47,7 @@ export default {
|
||||
</script>
|
||||
|
||||
<style lang="stylus" scoped>
|
||||
@import '~@style/mixin'
|
||||
@import '../../style/mixin'
|
||||
.navs_wraper
|
||||
width 100%
|
||||
padding 0 12%
|
||||
|
||||
@@ -63,10 +63,11 @@
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import KeyboardInput from '@components/keyboard-input'
|
||||
import {authlogin} from '@config/getData.js'
|
||||
import KeyboardInput from '../../../components/keyboard-input.vue'
|
||||
import {authlogin} from '../../../config/getData'
|
||||
import {encrypt} from '../../../main.js'
|
||||
export default {
|
||||
name: 'ModuleLogin',
|
||||
components: {
|
||||
KeyboardInput
|
||||
},
|
||||
@@ -220,7 +221,7 @@ export default {
|
||||
</script>
|
||||
|
||||
<style lang="stylus" scoped>
|
||||
@import '~@style/mixin'
|
||||
@import '../../style/mixin'
|
||||
.login-header
|
||||
top 0
|
||||
_wh(100%, 10vh)
|
||||
|
||||
@@ -71,12 +71,13 @@
|
||||
|
||||
<script>
|
||||
import { throttle } from 'lodash'
|
||||
import markerImage from '@images/new/agv.png'
|
||||
import carImage from '@images/new/car.png'
|
||||
import { getMapInfoByCode, getRouteInfo, queryMapAllStation } from '@config/getData.js'
|
||||
import canvasZoomDrag from '@config/canvasZoomDrag'
|
||||
import markerImage from '../../images/new/station.png'
|
||||
import carImage from '../../images/new/agv.png'
|
||||
import { getMapInfoByCode, getRouteInfo, queryMapAllStation } from '../../config/getData.js'
|
||||
import canvasZoomDrag from '../../config/canvasZoomDrag'
|
||||
import { mapGetters } from 'vuex'
|
||||
export default {
|
||||
name: 'ModuleMap',
|
||||
data () {
|
||||
return {
|
||||
canvas: null, // Canvas 元素
|
||||
@@ -105,13 +106,14 @@ export default {
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
...mapGetters(['agvObj']),
|
||||
carData () {
|
||||
let res = ''
|
||||
if (this.agvObj !== '') {
|
||||
res = JSON.parse(this.agvObj)
|
||||
}
|
||||
return res
|
||||
...mapGetters(['carPosition'])
|
||||
},
|
||||
watch: {
|
||||
carPosition: {
|
||||
handler() {
|
||||
this.redrawCanvas()
|
||||
},
|
||||
deep: true
|
||||
}
|
||||
},
|
||||
mixins: [canvasZoomDrag],
|
||||
@@ -120,21 +122,9 @@ export default {
|
||||
this.preloadCarImage()
|
||||
this.loadAllDataInParallel()
|
||||
document.addEventListener('click', this.handleDocumentClick)
|
||||
this.carPositionWatcher = this.$watch(
|
||||
() => this.carData,
|
||||
(newVal) => {
|
||||
if (newVal !== '') {
|
||||
this.redrawCanvas()
|
||||
}
|
||||
},
|
||||
{ deep: true }
|
||||
)
|
||||
},
|
||||
beforeDestroy () {
|
||||
document.removeEventListener('click', this.handleDocumentClick)
|
||||
if (this.carPositionWatcher) {
|
||||
this.carPositionWatcher()
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
preloadCarImage () {
|
||||
@@ -302,7 +292,7 @@ export default {
|
||||
}, 30),
|
||||
drawPath () {
|
||||
if (!this.pathData.length) return
|
||||
this.pathData.forEach((point, index) => {
|
||||
this.pathData.forEach(point => {
|
||||
const startX = (point.start_x - this.mapData.x) / this.mapData.resolution
|
||||
const startY = this.mapData.height - (point.start_y - this.mapData.y) / this.mapData.resolution
|
||||
const endX = (point.end_x - this.mapData.x) / this.mapData.resolution
|
||||
@@ -349,16 +339,20 @@ export default {
|
||||
})
|
||||
},
|
||||
drawCar () {
|
||||
if (!this.carData || !this.cachedImages.car || !this.mapData) return
|
||||
const carX = (this.carData.x - this.mapData.x) / this.mapData.resolution
|
||||
const carY = this.mapData.height - (this.carData.y - this.mapData.y) / this.mapData.resolution
|
||||
if (!this.cachedImages.car || !this.mapData) return
|
||||
const carX = (this.carPosition.x - this.mapData.x) / this.mapData.resolution
|
||||
const carY = this.mapData.height - (this.carPosition.y - this.mapData.y) / this.mapData.resolution
|
||||
this.ctx.save()
|
||||
this.ctx.translate(carX, carY)
|
||||
this.ctx.rotate(-this.carPosition.angle)
|
||||
this.ctx.drawImage(
|
||||
this.cachedImages.car,
|
||||
carX - 35,
|
||||
carY - 21,
|
||||
70,
|
||||
42
|
||||
-25,
|
||||
-25,
|
||||
50,
|
||||
50
|
||||
)
|
||||
// this.ctx.restore()
|
||||
},
|
||||
handleCanvasClick (event) {
|
||||
const rect = this.canvas.getBoundingClientRect()
|
||||
@@ -519,19 +513,6 @@ export default {
|
||||
background-color rgba(4, 33, 58, 70%)
|
||||
box-shadow inset 1px 1px 7px 2px #4d9bcd
|
||||
overflow hidden
|
||||
.map_tools
|
||||
position absolute
|
||||
top 0
|
||||
right 0
|
||||
.zoom_data
|
||||
width .6rem
|
||||
font-size .16rem
|
||||
height .32rem
|
||||
line-height .32rem
|
||||
color #00d9f3
|
||||
text-align center
|
||||
border-top 1px solid #009fde
|
||||
border-bottom 1px solid #009fde
|
||||
.point-popup
|
||||
position fixed
|
||||
background rgba(0, 0, 0, 70%)
|
||||
|
||||
@@ -52,9 +52,9 @@
|
||||
|
||||
<script>
|
||||
// import { throttle } from 'lodash'
|
||||
import markerImage from '@images/new/agv.png'
|
||||
import { getMapInfoByCode, getRouteInfo, queryMapAllStation } from '@config/mork.js'
|
||||
import canvasZoomDrag from '@config/canvasZoomDrag'
|
||||
import markerImage from '@images/new/station.png'
|
||||
import { getMapInfoByCode, getRouteInfo, queryMapAllStation } from '../../config/mork.js'
|
||||
import canvasZoomDrag from '../../config/canvasZoomDrag'
|
||||
export default {
|
||||
data () {
|
||||
return {
|
||||
|
||||
@@ -52,10 +52,10 @@
|
||||
|
||||
<script>
|
||||
import { throttle } from 'lodash'
|
||||
import markerImage from '@images/new/agv.png'
|
||||
import carImage from '@images/new/car.png'
|
||||
import { getMapInfoByCode, getRouteInfo, queryMapAllStation } from '@config/getData.js'
|
||||
import canvasZoomDrag from '@config/canvasZoomDrag'
|
||||
import markerImage from '@images/new/station.png'
|
||||
import carImage from '../../images/new/car.png'
|
||||
import { getMapInfoByCode, getRouteInfo, queryMapAllStation } from '../../config/getData.js'
|
||||
import canvasZoomDrag from '../../config/canvasZoomDrag'
|
||||
import { mapGetters } from 'vuex'
|
||||
export default {
|
||||
data () {
|
||||
|
||||
@@ -36,6 +36,7 @@
|
||||
<script>
|
||||
import WarnModal from './warn-modal.vue'
|
||||
export default {
|
||||
name: 'ModuleWarning',
|
||||
components: {
|
||||
WarnModal
|
||||
},
|
||||
|
||||
Reference in New Issue
Block a user