diff --git a/src/App.vue b/src/App.vue index 6a1dbdc..9799a5f 100644 --- a/src/App.vue +++ b/src/App.vue @@ -1,9 +1,6 @@ diff --git a/src/pages/modules/gl-map1.vue b/src/pages/modules/gl-map1.vue index 75e700e..82e8a6d 100644 --- a/src/pages/modules/gl-map1.vue +++ b/src/pages/modules/gl-map1.vue @@ -40,7 +40,9 @@ export default { viewSize: 20, animationId: null, lastUpdateTime: 0, - updateInterval: 100, // 限制更新频率,毫秒 + updateInterval: 800, // 限制更新频率,毫秒 + // 标记组件是否已销毁 + isDestroyed: false } }, computed: { @@ -76,19 +78,76 @@ export default { }, 1000); }, beforeDestroy () { - // 清理资源 + // 先标记组件已销毁,阻止新消息处理 + this.isDestroyed = true; + // 1. 清理WebSocket(先移除事件监听,再关闭连接) if (this.socket) { - this.socket.close(); + this.socket.onopen = null; + this.socket.onmessage = null; + this.socket.onclose = null; + this.socket.onerror = null; + this.socket.close(1000, '组件销毁'); + this.socket = null; } + // 2. 停止动画循环并清理 if (this.animationId) { cancelAnimationFrame(this.animationId); + this.animationId = null; } + // 3. 清理OrbitControls(关键!控制器有内部事件监听) + if (this.controls) { + this.controls.dispose(); // 控制器自带的清理方法,移除事件监听 + this.controls = null; + } + // 4. 清理Three.js场景对象(先从场景移除,再释放资源) + if (this.scene) { + // 移除点云网格 + if (this.pointCloudMesh) { + this.scene.remove(this.pointCloudMesh); + } + // 移除小车模型 + if (this.carMesh) { + this.scene.remove(this.carMesh); + } + // 清空场景(彻底释放所有子对象引用) + this.scene.clear(); + this.scene = null; + } + // 5. 释放Three.js渲染器及DOM + if (this.renderer) { + // 从DOM中移除canvas元素(避免残留DOM节点) + const container = this.$refs.canvasContainer; + if (container && this.renderer.domElement) { + container.removeChild(this.renderer.domElement); + } + // 释放渲染器资源 + this.renderer.dispose(); + this.renderer.forceContextLoss(); // 强制释放WebGL上下文(关键!) + this.renderer = null; + } + // 6. 释放几何体和材质(确保先移除引用) + if (this.pointCloudGeometry) { + this.pointCloudGeometry.dispose(); + this.pointCloudGeometry = null; + } + if (this.pointCloudMaterial) { + // 先清除材质的纹理引用(避免纹理无法释放) + this.pointCloudMaterial.map = null; + this.pointCloudMaterial.dispose(); + this.pointCloudMaterial = null; + } + // 7. 释放小车纹理(确保材质不再引用) + if (this.carTexture) { + this.carTexture.dispose(); + this.carTexture = null; + } + // 8. 移除窗口大小变化监听(避免重复监听) 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(); + // 9. 清空其他引用(帮助GC回收) + this.allPoints = []; + this.camera = null; + this.carMesh = null; + this.pointCloudMesh = null; }, methods: { /** @@ -130,7 +189,7 @@ export default { */ initControls() { this.controls = new OrbitControls(this.camera, this.renderer.domElement); - this.controls.enableRotate = true; // 禁用旋转,保持2D视图 + this.controls.enableRotate = false; // 禁用旋转,保持2D视图 this.controls.enableZoom = true; // 启用缩放 this.controls.enablePan = true; // 启用平移(拖动) this.controls.screenSpacePanning = true; // 2D平移模式 @@ -245,6 +304,8 @@ export default { }; this.socket.onmessage = (event) => { + // 组件已销毁则直接返回,不处理消息 + if (this.isDestroyed) return try { // 限制更新频率,优化性能 const now = Date.now(); @@ -254,7 +315,6 @@ export default { this.lastUpdateTime = now; const pointData = JSON.parse(event.data); - console.log(pointData.data) this.updatePointCloud(pointData.data); } catch (error) { console.error('解析点云数据失败:', error); @@ -277,6 +337,10 @@ export default { init () { const pointData = points.data this.updatePointCloud(pointData); + setTimeout(() => { + const arr = [{x: 2, y: 2}, {x: 4, y: 4}] + this.updatePointCloud(arr); + }, 2000) }, /** @@ -306,28 +370,46 @@ export default { 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 + let positions; + + if (!positionAttribute) { + // 首次初始化:创建缓冲区 + positions = new Float32Array(this.pointCount * 3); + this.pointCloudGeometry.setAttribute( + 'position', + new THREE.BufferAttribute(positions, 3) + ); + } else { + // 后续更新:扩展现有缓冲区(避免重建) + positions = positionAttribute.array; + const newLength = this.pointCount * 3; + if (newLength > positions.length) { + // 扩展时多预留20%空间,减少频繁扩展 + const newPositions = new Float32Array(Math.ceil(newLength * 1.2)); + newPositions.set(positions); // 复制原有数据 + positions = newPositions; + this.pointCloudGeometry.setAttribute( + 'position', + new THREE.BufferAttribute(positions, 3) + ); + } } - - // 标记属性需要更新 - positionAttribute.needsUpdate = true; + + // 填充新数据(仅更新新增部分,减少重复计算) + const startIndex = (this.pointCount - newUniquePoints.length) * 3; // 从新增点开始 + for (let i = 0; i < newUniquePoints.length; i++) { + const point = newUniquePoints[i]; + const index = startIndex + i * 3; + positions[index] = (point.x || 0) * this.pointScale; + positions[index + 1] = (point.y || 0) * this.pointScale; + positions[index + 2] = 0; + } + + // 标记更新 + this.pointCloudGeometry.getAttribute('position').needsUpdate = true; }, /**