diff --git a/web/.env b/web/.env index 41ee036..3fd9ab7 100644 --- a/web/.env +++ b/web/.env @@ -15,6 +15,8 @@ VITE_APP_CAPTCHA_ENABLE=false # 地图瓦片URL VITE_APP_MAP_TILE_URL = 'https://webrd04.is.autonavi.com/appmaptile?lang=zh_cn&size=1&scale=1&style=7&x={x}&y={y}&z={z}' +# 天地图瓦片URL:确少token、坐标偏移、叠加标记图层 +# VITE_APP_MAP_TILE_URL = 'https://t0.tianditu.gov.cn/vec_w/wmts?SERVICE=WMTS&REQUEST=GetTile&VERSION=1.0.0&LAYER=vec&STYLE=default&TILEMATRIXSET=w&TILEMATRIX={z}&TILEROW={y}&TILECOL={x}&FORMAT=tiles&tk=' #VITE_APP_MAP_TILE_URL = 'http://qtbj.icpcdev.site/roadmap/{z}/{x}/{y}.png' # 默认账户密码 diff --git a/web/src/views/HandDevice/Home/components/composables/useMapServices.ts b/web/src/views/HandDevice/Home/components/composables/useMapServices.ts index 0e69d4d..776ee77 100644 --- a/web/src/views/HandDevice/Home/components/composables/useMapServices.ts +++ b/web/src/views/HandDevice/Home/components/composables/useMapServices.ts @@ -3,11 +3,11 @@ */ // import type { Map } from 'ol' -import { ref, onUnmounted, reactive } from 'vue' +import { onUnmounted } from 'vue' import type { MapProps } from '../types/map.types' import { MapService } from '../services/map.service' import { MarkerService } from '../services/marker.service' -import { AnimationService } from '../services/animation.service' +// import { AnimationService } from '../services/animation.service' import { PopupService } from '../services/popup.service' import { TrajectoryService } from '../services/trajectory.service' import { FenceService } from '../services/fence.service' @@ -16,7 +16,7 @@ import { FenceDrawService } from '../services/fence-draw.service' interface ServiceInstances { mapService: MapService | null markerService: MarkerService | null - animationService: AnimationService | null + // animationService: AnimationService | null popupService: PopupService | null trajectoryService: TrajectoryService | null fenceService: FenceService | null @@ -28,7 +28,7 @@ export const useMapServices = () => { const services: ServiceInstances = { mapService: null, markerService: null, - animationService: null, + // animationService: null, popupService: null, trajectoryService: null, fenceService: null, @@ -48,7 +48,7 @@ export const useMapServices = () => { // 重新初始化服务,确保markerService有地图实例 services.mapService = mapService services.markerService = new MarkerService(mapService.map) - services.animationService = new AnimationService(mapService.map) + // services.animationService = new AnimationService(mapService.map) services.popupService = new PopupService() services.trajectoryService = new TrajectoryService(mapService.map) services.fenceService = new FenceService(mapService.map) @@ -56,8 +56,6 @@ export const useMapServices = () => { // 创建marker图层 services.markerService.createMarkerLayer(props) - // 创建波纹图层 - services.animationService.createRippleLayer(props.markers || [], props.enableCluster) // 创建轨迹图层 services.trajectoryService.createTrajectoryLayer() // 创建围栏图层 @@ -72,10 +70,10 @@ export const useMapServices = () => { const setMarkersVisible = (visible: boolean) => { if (visible) { services.markerService?.show() - services.animationService?.show() + // services.animationService?.show() } else { services.markerService?.hide() - services.animationService?.hide() + // services.animationService?.hide() } } @@ -135,19 +133,16 @@ export const useMapServices = () => { * 更新标记数据 */ const updateMarkers = (markers: any[], currentProps?: MapProps) => { - if (services.markerService && services.animationService) { + if (services.markerService) { const map = services.mapService?.getMap() - const enableCluster = currentProps?.enableCluster ?? true + if (map) { // console.log('updateMarkers', markers) - // 更新marker service(这会创建新的layer) + // 更新marker service(这可能会创建新的layer) services.markerService.createMarkerLayer({ ...currentProps, markers }) - - // 重新创建波纹图层 - services.animationService.createRippleLayer(markers, enableCluster) } } } @@ -169,7 +164,7 @@ export const useMapServices = () => { const servicesToDestroy = [ services.mapService, services.markerService, - services.animationService, + services.trajectoryService, services.fenceService, services.fenceDrawService @@ -184,7 +179,6 @@ export const useMapServices = () => { // 重置服务字段(保持 reactive 对象引用不变) services.mapService = null services.markerService = null - services.animationService = null services.popupService = null services.trajectoryService = null services.fenceService = null 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 6772d8b..587ce57 100644 --- a/web/src/views/HandDevice/Home/components/constants/map.constants.ts +++ b/web/src/views/HandDevice/Home/components/constants/map.constants.ts @@ -29,7 +29,6 @@ export const STATUS_DICT = { // 状态优先级定义 (数字越小优先级越高) export const STATUS_PRIORITY = { - gasStatus_2: 1, gasStatus_1: 2, // battery_2: 3, @@ -63,13 +62,37 @@ export const MAP_DEFAULTS = { // 动画配置 export const ANIMATION_CONFIG = { - duration: 3, // 动画周期(秒) - rippleCount: 5, // 波纹圈数量 - phaseOffset: 0.6, // 波纹圈错开时间(秒) - targetFPS: 60, // 目标帧率 - clusterThreshold: 12, // 聚合阈值 - minRadius: 6, // 最小半径 - maxRadius: 31, // 最大半径 + /** + * 动画周期(秒) + */ + duration: 3, + /** + * 波纹圈数量 + */ + rippleCount: 5, + /** + * 波纹圈错开时间(秒) + */ + phaseOffset: 0.6, + /** + * 目标帧率 + */ + targetFPS: 60, + /** + * 聚合阈值 + */ + clusterThreshold: 12, + /** + * 最小半径 + */ + minRadius: 6, + /** + * 最大半径 + */ + maxRadius: 31, + /** + * 最小透明度阈值 + */ minOpacity: 0.05 // 最小透明度阈值 } export const DEFAULT_FENCES = [] as FenceData[] 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 613d2d0..c386957 100644 --- a/web/src/views/HandDevice/Home/components/services/animation.service.ts +++ b/web/src/views/HandDevice/Home/components/services/animation.service.ts @@ -18,60 +18,20 @@ export class AnimationService { private animationTimer: number | null = null private map: Map | null = null - private enableCluster: boolean = true constructor(map: Map) { this.map = map + this.createRippleLayer() } - updateData(markers: MarkerData[]) { - const source = this.rippleLayer?.getSource() - if (source) { - source?.clear() - - // 为每个标记添加波纹效果 - markers.forEach((marker) => { - 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) - // this.rippleLayer?.setSource(source) - }) - } - } - /** + /** * 创建波纹图层 - * enableCluster 是否启用聚合 */ - createRippleLayer( - markers: MarkerData[], - - enableCluster: boolean = true - ): VectorLayer { - if (this.rippleLayer && this.map) { - this.updateData(markers) - return this.rippleLayer - } - this.enableCluster = enableCluster + private createRippleLayer() { const source = new VectorSource() this.rippleLayer = new VectorLayer({ source: source, zIndex: 1, style: (feature) => { - // 检查当前缩放级别,如果缩放级别较低(聚合状态),不显示波纹 - const currentZoom = this.map?.getView().getZoom() || 0 - - // 如果启用了聚合且zoom级别较低,不显示波纹 - if (this.enableCluster && currentZoom < ANIMATION_CONFIG.clusterThreshold) { - return [] // 不显示波纹 - } - const startTime = feature.get('animationStart') const color = feature.get('rippleColor') const elapsed = (Date.now() - startTime) / 1000 // 秒 @@ -119,13 +79,36 @@ export class AnimationService { return styles } }) - this.updateData(markers) if (this.map) { this.map?.addLayer(this.rippleLayer) } + } + + clear() { + const source = this.rippleLayer?.getSource() + source?.clear() + } + add(marker: MarkerData) { + const source = this.rippleLayer?.getSource() + const feature = new Feature({ + geometry: new Point(fromLonLat(marker.coordinates)), + markerData: marker + }) - return this.rippleLayer + // 设置动画开始时间 + feature.set('animationStart', Date.now()) + feature.set('rippleColor', marker.statusColor) + + source?.addFeature(feature) + } + addAll(markers: MarkerData[]) { + this.clear() + // 为每个标记添加波纹效果 + markers.forEach((marker) => { + this.add(marker) + }) } + show() { this.rippleLayer?.setVisible(true) this.startAnimation() @@ -162,29 +145,15 @@ export class AnimationService { } /** - * 更新波纹图层 - */ - updateRipples(): void { - if (this.rippleLayer) { - this.rippleLayer.getSource()?.changed() - } - } - - /** - * 获取波纹图层 - */ - getRippleLayer(): VectorLayer | null { - return this.rippleLayer - } - - /** * 销毁动画服务 */ destroy(): void { + this.stopAnimation() + if (this.rippleLayer && this.map) { this.map?.removeLayer(this.rippleLayer) } - this.stopAnimation() + this.rippleLayer = null this.map = null } 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 53daa7b..dafecd4 100644 --- a/web/src/views/HandDevice/Home/components/services/marker.service.ts +++ b/web/src/views/HandDevice/Home/components/services/marker.service.ts @@ -9,29 +9,31 @@ import { Point } from 'ol/geom' import { Style } from 'ol/style' import { fromLonLat } from 'ol/proj' +import { AnimationService } from './animation.service' + import type { MarkerData, MapProps } from '../types/map.types' -import { - createMarkerStyle, - getClusterMarkerData, - getStatusColor -} from '../utils/map.utils' +import { createMarkerStyle, getClusterMarkerData, getStatusColor } from '../utils/map.utils' import { ANIMATION_CONFIG } from '../constants/map.constants' export class MarkerService { private map: Map | null = null markerLayer: VectorLayer | null = null - // 当前图层模式(single或cluster聚合) + // 当前图层模式(single或cluster聚合):避免重复创建图层 private currentLayerMode: 'single' | 'cluster' | '' = '' private vectorSource: VectorSource + private animationService: AnimationService | null = null constructor(map: Map) { this.map = map this.vectorSource = new VectorSource() + this.animationService = new AnimationService(map) } show() { this.markerLayer?.setVisible(true) + this.animationService?.show() } hide() { this.markerLayer?.setVisible(false) + this.animationService?.hide() } /** * 创建标记图层 @@ -69,6 +71,8 @@ export class MarkerService { */ // : VectorLayer private createMarkerLayerFromProps(props: MapProps) { + // console.log('createMarkerLayerFromProps') + this.animationService?.clear() this.updateData(props) // 检查是否应该强制使用单个marker模式 @@ -101,11 +105,14 @@ export class MarkerService { if (!features || features.length === 0) { return new Style() // 返回空样式,隐藏无效的feature } + // console.log('聚合元素', features) if (features.length === 1) { // 单个marker const markerData: MarkerData = features[0].get('markerData') + this.animationService?.add(markerData) + return markerData ? createMarkerStyle(markerData.statusColor || '') : new Style() } else { // 聚合marker @@ -117,17 +124,19 @@ export class MarkerService { } }) } else { + // markers可能变化,必须更新 + this.animationService?.addAll(props.markers || []) + if (this.currentLayerMode === 'single') return this.currentLayerMode = 'single' + // console.log('基础marker') newLayer = new VectorLayer({ source: this.vectorSource, zIndex: 1, renderOrder: (a, b) => { - // console.log('renderOrder',a,b.get('markerData').time); - // 按xxx属性排列 return b.get('markerData').statusPriority - a.get('markerData').statusPriority } @@ -136,16 +145,12 @@ export class MarkerService { if (this.markerLayer) { const isVisible = this.markerLayer?.getVisible() || false - newLayer.setVisible(isVisible) + newLayer.setVisible(isVisible) // 新图层保持当前可见状态 this.map?.removeLayer(this.markerLayer) } this.markerLayer = newLayer this.map?.addLayer(this.markerLayer) - - // 获取所有图层 - // const layers = this.map?.getLayers().getArray() || [] - // console.log('this.currentLayerMode', this.currentLayerMode, layers) } /** @@ -162,6 +167,7 @@ export class MarkerService { */ destroy(): void { this.markerLayer = null + this.animationService?.destroy() // this.currentProps = null this.map = null }