news 2026/4/17 3:11:58

Vue项目中天地图动态标注的添加与删除实践

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
Vue项目中天地图动态标注的添加与删除实践

1. 天地图与Vue结合的基础准备

在Vue项目中使用天地图API前,需要先完成基础的环境配置。我推荐使用npm安装天地图JavaScript API的方式,这样能更好地与现代前端工程化开发流程结合。首先在项目中执行:

npm install tdt-map

安装完成后,在main.js中全局引入天地图资源。这里有个小技巧,建议把天地图密钥放在环境变量中管理:

import T from 'tdt-map' import '../node_modules/tdt-map/dist/tdt.min.css' Vue.prototype.$T = T Vue.prototype.$tdtKey = process.env.VUE_APP_TDT_KEY

初始化地图组件时,我习惯把地图实例挂载到Vue实例上,方便全局调用。在组件mounted钩子中这样写:

mounted() { this.map = new this.$T.Map('mapContainer', { center: new this.$T.LngLat(116.404, 39.915), zoom: 11, minZoom: 5, maxZoom: 18 }) }

这里有个容易踩的坑:天地图容器需要设置明确的高度。我建议在CSS中使用vh单位,确保在不同设备上都能正常显示:

#mapContainer { width: 100%; height: 80vh; margin: 0; padding: 0; }

2. 动态标注的创建与管理

2.1 标注的批量创建

实际项目中,我们通常需要批量添加标注点。我封装了一个可复用的方法,支持自定义图标和点击事件:

addMarkers(points, iconConfig, clickHandler) { const markers = [] points.forEach(item => { const marker = new this.$T.Marker( new this.$T.LngLat(item.lng, item.lat), { icon: new this.$T.Icon({ iconUrl: iconConfig.url, iconSize: new this.$T.Point(iconConfig.width, iconConfig.height) }) } ) marker.customId = item.id // 自定义标识 this.map.addOverLay(marker) if (clickHandler) { marker.addEventListener('click', () => { clickHandler(item, marker) }) } markers.push(marker) }) return markers }

使用时可以这样调用:

const hospitals = this.addMarkers( [ {id: 'h1', lng: 116.404, lat: 39.915, name: '协和医院'}, {id: 'h2', lng: 116.428, lat: 39.903, name: '301医院'} ], { url: require('@/assets/hospital.png'), width: 32, height: 32 }, (item, marker) => { this.showHospitalInfo(item) } )

2.2 标注的自定义属性

为了后续能精准控制特定标注,我们需要给标注添加自定义属性。除了上面代码中的customId,还可以扩展更多元数据:

marker.metaData = { type: 'hospital', district: '海淀区', level: '三甲' }

这样在后续操作时,我们不仅能通过ID查找标注,还能按类型、区域等属性进行筛选。

3. 精准删除标注的实现方案

3.1 基于标识的删除方法

原始文章展示了通过遍历删除的方式,这里我优化了一个更高效的版本:

removeMarkersByIds(ids) { const overlays = this.map.getOverlays() overlays.forEach(overlay => { if (overlay.customId && ids.includes(overlay.customId)) { this.map.removeOverLay(overlay) } }) }

这个方法支持批量删除,调用时只需传入要删除的标注ID数组:

// 删除ID为h1和h2的标注 this.removeMarkersByIds(['h1', 'h2'])

3.2 基于条件的动态筛选

更灵活的做法是根据条件动态筛选要删除的标注:

removeMarkersByCondition(conditionFn) { const overlays = this.map.getOverlays() overlays.forEach(overlay => { if (conditionFn(overlay)) { this.map.removeOverLay(overlay) } }) }

使用示例:

// 删除所有类型为医院的标注 this.removeMarkersByCondition( overlay => overlay.metaData?.type === 'hospital' ) // 删除海淀区的三甲医院 this.removeMarkersByCondition( overlay => overlay.metaData?.district === '海淀区' && overlay.metaData?.level === '三甲' )

4. 高级应用:标注的状态管理

4.1 使用Vuex管理标注状态

在复杂项目中,建议使用Vuex集中管理标注状态。首先定义store:

// store/modules/map.js const state = { markers: [], visibleMarkerIds: [] } const mutations = { ADD_MARKERS(state, markers) { state.markers = [...state.markers, ...markers] }, TOGGLE_MARKERS(state, {ids, visible}) { if (visible) { state.visibleMarkerIds = [...new Set([...state.visibleMarkerIds, ...ids])] } else { state.visibleMarkerIds = state.visibleMarkerIds.filter(id => !ids.includes(id)) } } }

然后在组件中使用watch监听状态变化:

watch: { 'mapStore/visibleMarkerIds': { handler(newVal, oldVal) { // 找出需要新增的标注 const toAdd = this.mapStore.markers.filter( m => newVal.includes(m.customId) && !oldVal.includes(m.customId) ) // 找出需要删除的标注 const toRemove = oldVal.filter(id => !newVal.includes(id)) // 执行更新 if (toAdd.length) this.addMarkers(toAdd) if (toRemove.length) this.removeMarkersByIds(toRemove) }, deep: true } }

4.2 标注的动画效果

为提升用户体验,可以为标注添加显隐动画。这里实现一个淡入淡出效果:

async fadeMarker(marker, show = true, duration = 500) { const step = 20 const delta = 1 / (duration / step) if (show) { marker.setOpacity(0) this.map.addOverLay(marker) for (let op = 0; op <= 1; op += delta) { await new Promise(resolve => setTimeout(resolve, step)) marker.setOpacity(op) } } else { for (let op = 1; op >= 0; op -= delta) { await new Promise(resolve => setTimeout(resolve, step)) marker.setOpacity(op) } this.map.removeOverLay(marker) } }

使用时可以这样调用:

// 淡入显示 this.fadeMarker(marker, true) // 淡出隐藏 this.fadeMarker(marker, false)

5. 性能优化实践

5.1 标注的聚类显示

当地图缩放级别较小时,可以使用标注聚类来提升性能。先安装聚类插件:

npm install tdt-map-cluster

然后在项目中这样使用:

import MarkerCluster from 'tdt-map-cluster' // 创建聚类实例 const cluster = new MarkerCluster(this.map, { gridSize: 80, maxZoom: 15, styles: [{ url: require('@/assets/cluster.png'), size: new this.$T.Point(40, 40), textColor: '#fff' }] }) // 添加标注到聚类管理器 cluster.addMarkers(markers) // 需要删除时 cluster.removeMarkers(markersToRemove)

5.2 可视区域优化

当地图标注很多时,可以只渲染可视区域内的标注:

updateVisibleMarkers() { const bounds = this.map.getBounds() this.allMarkers.forEach(marker => { const position = marker.getLngLat() if (bounds.contains(position)) { if (!this.map.getOverlays().includes(marker)) { this.map.addOverLay(marker) } } else { this.map.removeOverLay(marker) } }) } // 监听地图移动事件 this.map.addEventListener('moveend', this.updateVisibleMarkers)

6. 常见问题解决方案

在实际开发中,我遇到过几个典型问题值得分享。首先是天地图标注点击事件冒泡问题,当地图上既有标注点击又有地图点击时,会出现事件冲突。解决方案是:

marker.addEventListener('click', e => { e.stopPropagation() // 处理标注点击逻辑 })

其次是标注z-index的控制问题。天地图的标注默认是按添加顺序决定叠放层次,如果需要手动控制:

// 将标注置顶 marker.setTop(true) // 设置具体z-index marker.setZIndex(100)

还有一个常见问题是内存泄漏。在Vue组件销毁时,务必清理所有标注和事件监听:

beforeDestroy() { // 移除所有标注 this.map.clearOverlays() // 移除事件监听 this.map.removeEventListener('moveend', this.updateVisibleMarkers) // 如果有聚类实例 if (this.cluster) { this.cluster.clearMarkers() } }

7. 完整示例代码

最后分享一个完整的Vue单文件组件示例,实现了标注的增删改查:

<template> <div> <div id="mapContainer"></div> <div class="control-panel"> <button @click="addHospitalMarkers">添加医院</button> <button @click="removeAllMarkers">清除所有</button> <div v-for="marker in markers" :key="marker.id"> <input type="checkbox" v-model="marker.visible" @change="toggleMarker(marker)" > {{ marker.name }} </div> </div> </div> </template> <script> export default { data() { return { map: null, markers: [ {id: 'h1', lng: 116.404, lat: 39.915, name: '协和医院', visible: true}, {id: 'h2', lng: 116.428, lat: 39.903, name: '301医院', visible: true} ], markerInstances: {} } }, mounted() { this.initMap() this.addHospitalMarkers() }, methods: { initMap() { this.map = new this.$T.Map('mapContainer', { center: new this.$T.LngLat(116.404, 39.915), zoom: 12 }) }, addHospitalMarkers() { this.markers.forEach(item => { if (item.visible && !this.markerInstances[item.id]) { const marker = new this.$T.Marker( new this.$T.LngLat(item.lng, item.lat), { icon: new this.$T.Icon({ iconUrl: require('@/assets/hospital.png'), iconSize: new this.$T.Point(32, 32) }) } ) marker.customId = item.id this.map.addOverLay(marker) this.markerInstances[item.id] = marker } }) }, toggleMarker(marker) { if (marker.visible) { this.addMarker(marker) } else { this.removeMarker(marker.id) } }, addMarker(markerData) { if (this.markerInstances[markerData.id]) return const marker = new this.$T.Marker( new this.$T.LngLat(markerData.lng, markerData.lat), { icon: new this.$T.Icon({ iconUrl: require('@/assets/hospital.png'), iconSize: new this.$T.Point(32, 32) }) } ) marker.customId = markerData.id this.map.addOverLay(marker) this.markerInstances[markerData.id] = marker }, removeMarker(markerId) { if (!this.markerInstances[markerId]) return this.map.removeOverLay(this.markerInstances[markerId]) delete this.markerInstances[markerId] }, removeAllMarkers() { Object.values(this.markerInstances).forEach(marker => { this.map.removeOverLay(marker) }) this.markerInstances = {} this.markers.forEach(m => m.visible = false) } }, beforeDestroy() { this.map.clearOverlays() } } </script> <style> #mapContainer { width: 100%; height: 80vh; } .control-panel { position: absolute; top: 20px; right: 20px; background: white; padding: 10px; z-index: 1000; } </style>
版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/4/17 3:11:57

2025年03月CCF-GESP编程能力等级认证Python编程六级真题解析

本文收录于专栏《Python等级认证CCF-GESP真题解析》,专栏总目录:点这里,订阅后可阅读专栏内所有文章。 一、单选题(每题 2 分,共 30 分) 第 1 题 在面向对象编程中,类是一种重要的概念。下面关于类的描述中,不正确的是 ( )。 A. 类是一个抽象的概念,用于描述具有相…

作者头像 李华
网站建设 2026/4/17 3:11:33

高效判断点与图形位置关系的算法解析(矩形、椭圆、多边形)

1. 点与图形位置关系的高效判断原理 判断一个点是否位于特定图形内部&#xff0c;是计算机图形学、游戏开发和地理信息系统中的基础问题。想象一下&#xff0c;当你在手机地图上点击某个位置时&#xff0c;系统需要快速判断这个点是否在某个建筑物&#xff08;多边形&#xff0…

作者头像 李华
网站建设 2026/4/17 3:04:16

Python数据分析项目实战(060)——Python数据分析与统计综合案例

版权声明 本文原创作者:谷哥的小弟 作者博客地址:http://blog.csdn.net/lfdfhl 项目概述 本项目适合Python数据分析数据分析新手,通过分析城市空气质量数据,综合运用NumPy、Pandas和Matplotlib库,掌握从数据加载、清洗、分析到可视化的完整流程。 本项目主要技术: 如何…

作者头像 李华
网站建设 2026/4/17 3:03:11

春秋云境CVE-2021-34257

1.阅读靶场介绍 这里能得到的信息 就是文件上传 构造木马php文件 2.启动靶场 如下所示 3.寻找后台 这里博主使用的url如下 https://eci-2zeizdkm339qzk1nbhsk.cloudeci1.ichunqiu.com/index.php/admin/login 页面如下所示 这里的账号密码是:adminadmin.com/admin 4.poc…

作者头像 李华
网站建设 2026/4/17 3:02:11

2026届毕业生推荐的六大AI辅助论文助手实际效果

Ai论文网站排名&#xff08;开题报告、文献综述、降aigc率、降重综合对比&#xff09; TOP1. 千笔AI TOP2. aipasspaper TOP3. 清北论文 TOP4. 豆包 TOP5. kimi TOP6. deepseek 围绕大语言模型的高效训练与推理&#xff0c;DeepSeek系列论文展开了研究。其核心贡献是以提…

作者头像 李华