diff --git a/web/src/views/HandDevice/Home/components/OpenLayerMap.vue b/web/src/views/HandDevice/Home/components/OpenLayerMap.vue index e7416da..92e4620 100644 --- a/web/src/views/HandDevice/Home/components/OpenLayerMap.vue +++ b/web/src/views/HandDevice/Home/components/OpenLayerMap.vue @@ -289,7 +289,7 @@ const refreshFences = () => { watch( () => props.markers, (newMarkers, oldMarkers) => { - console.log('markers changed', newMarkers, oldMarkers) + // console.log('markers changed', newMarkers, oldMarkers) services.markerService?.updateData(newMarkers) if (oldMarkers == undefined || newMarkers.length !== oldMarkers.length) { fitToMarkers() diff --git a/web/src/views/HandDevice/Home/components/constants/map.constants.ts b/web/src/views/HandDevice/Home/components/constants/map.constants.ts index f0d1f5c..6d50b4e 100644 --- a/web/src/views/HandDevice/Home/components/constants/map.constants.ts +++ b/web/src/views/HandDevice/Home/components/constants/map.constants.ts @@ -61,19 +61,19 @@ export const ANIMATION_CONFIG = { /** * 动画周期(秒) */ - duration: 3, + duration: 2.4, /** * 波纹圈数量 */ - rippleCount: 5, + rippleCount: 3, /** * 波纹圈错开时间(秒) */ - phaseOffset: 0.6, + phaseOffset: 0.8, /** * 目标帧率 */ - targetFPS: 60, + targetFPS: 25, /** * 聚合图层阈值 */ @@ -81,11 +81,11 @@ export const ANIMATION_CONFIG = { /** * 最小半径 */ - minRadius: 6, + minRadius: 2, /** * 最大半径 */ - maxRadius: 31, + maxRadius: 25, /** * 最小透明度阈值 */ diff --git a/web/src/views/HandDevice/Home/components/services/animation.service.ts b/web/src/views/HandDevice/Home/components/services/animation.service.ts index af323ff..b31a432 100644 --- a/web/src/views/HandDevice/Home/components/services/animation.service.ts +++ b/web/src/views/HandDevice/Home/components/services/animation.service.ts @@ -22,6 +22,9 @@ export class AnimationService { this.map = map this.createRippleLayer() } + private transparentFill: Fill = new Fill({ + color: 'transparent' + }) /** * 创建波纹图层 */ @@ -63,9 +66,7 @@ export class AnimationService { new Style({ image: new Circle({ radius: radius, - fill: new Fill({ - color: 'transparent' - }), + fill: this.transparentFill, stroke: new Stroke({ color: strokeColor, width: Math.max(1, 3 - i * 0.4) // 动态调整线宽 @@ -86,21 +87,9 @@ export class AnimationService { clear() { const source = this.rippleLayer?.getSource() - source?.clear() + source?.clear(true) } - add(marker: MarkerData) { - const source = this.rippleLayer?.getSource() - const feature = new Feature({ - geometry: new Point(fromLonLat(marker.coordinates)), - markerData: marker - }) - // 设置动画开始时间 - feature.set('animationStart', Date.now()) - feature.set('rippleColor', marker.statusColor) - - source?.addFeature(feature) - } addAll(markers: MarkerData[]) { this.clear() const source = this.rippleLayer?.getSource() diff --git a/web/src/views/HandDevice/Home/components/services/map.service.ts b/web/src/views/HandDevice/Home/components/services/map.service.ts index 2c9b8e4..c71bcf5 100644 --- a/web/src/views/HandDevice/Home/components/services/map.service.ts +++ b/web/src/views/HandDevice/Home/components/services/map.service.ts @@ -3,8 +3,11 @@ */ import { Map, View } from 'ol' -import { Tile as TileLayer } from 'ol/layer' -import { OSM, XYZ } from 'ol/source' +// import { Tile as TileLayer } from 'ol/layer' +import WebGLTileLayer from 'ol/layer/WebGLTile'; +import XYZSource from 'ol/source/XYZ'; + +// import { OSM, XYZ } from 'ol/source' import { fromLonLat, transformExtent } from 'ol/proj' import { boundingExtent } from 'ol/extent' @@ -15,7 +18,7 @@ import type { MapProps } from '../types/map.types' export class MapService { map: Map | null = null - tileLayer: TileLayer | null = null + tileLayer: WebGLTileLayer | null = null popupOverlay: Overlay | null = null /** @@ -46,13 +49,14 @@ export class MapService { /** * 创建瓦片图层 */ - private createTileLayer(props: MapProps): TileLayer { - const source = new XYZ({ + private createTileLayer(props: MapProps): WebGLTileLayer { + const source = new XYZSource({ url: props.tileUrl!, + tileSize: 256, maxZoom: props.maxZoom, minZoom: props.minZoom }) - return new TileLayer({ + return new WebGLTileLayer({ source: source }) } diff --git a/web/src/views/HandDevice/Home/components/services/marker.service.ts b/web/src/views/HandDevice/Home/components/services/marker.service.ts index 3ea81cd..fc2cbc1 100644 --- a/web/src/views/HandDevice/Home/components/services/marker.service.ts +++ b/web/src/views/HandDevice/Home/components/services/marker.service.ts @@ -1,7 +1,8 @@ /** * 标记服务类 */ -import type { Map } from 'ol' +import { nextTick } from 'vue' +import type { Map as OpenLayersMap } from 'ol' import { Vector as VectorLayer } from 'ol/layer' import { Vector as VectorSource, Cluster } from 'ol/source' import { Feature } from 'ol' @@ -17,13 +18,13 @@ import { createMarkerStyle, getClusterMarkerData, getStatusColor } from '../util // 防抖 import { debounce } from 'lodash-es' export class MarkerService { - private map: Map | null = null + private map: OpenLayersMap | null = null markerLayer: VectorLayer | null = null private vectorSource: VectorSource private animationService: AnimationService | null = null - constructor(map: Map) { + constructor(map: OpenLayersMap) { this.map = map this.vectorSource = new VectorSource() this.animationService = new AnimationService(map) @@ -37,14 +38,19 @@ export class MarkerService { this.markerLayer?.setVisible(false) this.animationService?.hide() } - /** * 更新标记数据 */ - updateData(markers: MarkerData[]): void { + async updateData(markers: MarkerData[]) { + console.time('animationService clear') this.animationService?.clear() - this.vectorSource.clear() -// debugger + console.timeEnd('animationService clear') + await nextTick() + console.time('vectorSource clear') + this.vectorSource.clear(true) + console.timeEnd('vectorSource clear') + await nextTick() + console.time('create features') const features: Feature[] = [] markers.forEach((marker) => { const feature = new Feature({ @@ -56,7 +62,12 @@ export class MarkerService { feature.setStyle(createMarkerStyle(statusColor)) features.push(feature) }) + console.timeEnd('create features') + await nextTick() + console.time('add features') this.vectorSource.addFeatures(features) + + console.timeEnd('add features') this.getSinglePointsInView() } setClusterDistance = debounce(() => { @@ -72,9 +83,9 @@ export class MarkerService { } else if (zoom <= 6) { distance = 80 } else if (zoom <= 10) { - distance = 30 + distance = 50 } else if (zoom <= 16) { - distance = 30 + distance = 20 } else if (zoom <= 17) { distance = 10 } @@ -94,7 +105,7 @@ export class MarkerService { // 获取聚合图层的源 const clusterSource = clusterLayer.getSource() - if (!clusterSource) return + if (!clusterSource) return // 获取当前视图范围 const view = map.getView() @@ -105,7 +116,7 @@ export class MarkerService { // console.log('featuresInView',featuresInView) featuresInView.forEach((clusterFeature) => { // 关键:获取聚合要素中包含的所有原始要素 - const originalFeatures = clusterFeature.get('features') + const originalFeatures = clusterFeature.get('features') // console.log('originalFeatures',originalFeatures); if (originalFeatures && originalFeatures.length === 1) { @@ -115,7 +126,6 @@ export class MarkerService { } }) this.animationService?.addAll(singlePoints || []) - }, 300) /** @@ -142,7 +152,7 @@ export class MarkerService { // 确保features存在且不为空 if (!features || features.length === 0) { - return new Style() // 返回空样式,隐藏无效的feature + return [] // 返回空样式,隐藏无效的feature } // console.log('聚合元素', features) @@ -180,7 +190,8 @@ export class MarkerService { destroy(): void { this.markerLayer = null this.animationService?.destroy() - + this.animationService = null + this.map = null } } diff --git a/web/src/views/HandDevice/Home/components/utils/map.utils.ts b/web/src/views/HandDevice/Home/components/utils/map.utils.ts index ba025fc..c3261e7 100644 --- a/web/src/views/HandDevice/Home/components/utils/map.utils.ts +++ b/web/src/views/HandDevice/Home/components/utils/map.utils.ts @@ -158,7 +158,7 @@ export const createMarkerStyle = ( styleCache[key]= new Style({ image: new Circle({ - radius: Math.min(20 + clusterSize/4, 40), + radius: Math.min(20 + clusterSize/20, 40), fill: new Fill({ color: color + '80' // 添加透明度 }), diff --git a/web/src/views/HandDevice/Home/index.vue b/web/src/views/HandDevice/Home/index.vue index c75211b..b34e8f8 100644 --- a/web/src/views/HandDevice/Home/index.vue +++ b/web/src/views/HandDevice/Home/index.vue @@ -170,7 +170,38 @@ const filterMarkers = computed(() => { const filterMarkers2 = function getFilterMarkers2() { var arr: MarkerData[] = [] var nowTime = dayjs().format('YYYY-MM-DD HH:mm:ss') - for (let i = 0; i < 50000; i++) { + const arr2=[ + { + statusStr: 'normal', + statusColor: '#67c23a', + statusLabel: '正常', + statusPriority: 7, + + },{ + statusStr: 'offline', + statusColor: '#909399', + statusLabel: '离线', + statusPriority: 6, + + },{ + statusStr: 'fenceStatus_1', + statusColor: '#e6a23c', + statusLabel: '电子围栏报警', + statusPriority: 5, + },{ + statusStr: 'gasStatus_1', + statusColor: '#f56c6c', + statusLabel: '报警', + statusPriority: 4, + },{ + statusStr: 'batteryStatus_1', + statusColor: '#f56c6c', + statusLabel: '电池报警', + statusPriority: 3, + } + ] + for (let i = 0; i < 10000; i++) { + const statusInfo = arr2[Math.floor(Math.random() * arr2.length)] const lon = 100 + Math.random() * 2 const lat = 30 + Math.random() * 2 arr.push({ @@ -189,18 +220,18 @@ const filterMarkers2 = function getFilterMarkers2() { gasChemical: 'CO', batteryAlarmValue: 20, accuracy: 1, - alarmLevel: -1, + alarmLevel: 1, maxAlarmLevel: null, firstValue: null, maxValue: null, gpsType: 0, - gasStatus: 0, + gasStatus: 2, alarmId: 200, batteryStatus: 0, batteryStatusAlarmId: null, fenceStatus: 0, fenceAlarmId: null, - onlineStatus: 0, + onlineStatus: 1, enableStatus: 1, distance: null, maxDistance: null, @@ -209,10 +240,10 @@ const filterMarkers2 = function getFilterMarkers2() { talarmEnd: null, coordinates: [lon, lat], timeStr: nowTime, - statusStr: 'offline', - statusColor: '#909399', - statusLabel: '离线', - statusPriority: 7, + statusStr: statusInfo.statusStr, + statusColor: statusInfo.statusColor, + statusLabel: statusInfo.statusLabel, + statusPriority: statusInfo.statusPriority, expanded: false }) }