概述
在现代前端GIS应用开发中,地图标记点(Marker)的管理是核心功能之一。本文将详细介绍如何基于Vue3、Ant Design Vue 4.0和高德地图API,实现一个高度可配置、支持多类型标记点、具备丰富交互功能的标记点管理系统。
技术栈
Vue3:使用Composition API进行逻辑封装
JavaScript:主要开发语言
Ant Design Vue 4.0:UI组件库
高德地图API:地图展示与交互基础
事件总线:组件间通信机制
核心功能设计
1. 标记点类型配置系统
首先,我们创建一个中央化的配置系统来管理不同类型的标记点:
// marker-config.js import { markerEnum } from "@/utils/enum"; export const markerConfigs = { [markerEnum.SXJ]: { key: markerEnum.SXJ, idKey: "id", // 唯一标识字段 textKey: "name", // 文本标签字段 isOnLine: (data) => true, // 在线状态检测 iconFilter: (data, isHover) => { let image = null; image = isHover ? sxjHoverMarker : sxjMarker; return getMarkerIcon(image); }, zIndex: 35, // 图层层级 }, [markerEnum.ZBJL]: { key: markerEnum.ZBJL, isOnLine: (data) => true, iconFilter: (data, isHover = false) => { let image = null; image = isHover ? zbjlHoverMarker : zbjlMarker; return getMarkerIcon(image); }, idKey: "id", textKey: "license", zIndex: 35, }, // 其他标记点类型... }; // 获取配置的辅助函数 export const getMarkerConfig = (type) => markerConfigs[type];2. 自定义Hook封装
使用Vue3的Composition API封装标记点管理逻辑:
// useMarker.js export default () => { let textLabel = null; const markerTypes = ref([]); const markers = ref([]); const selectMarkerId = ref(null); // 创建单个标记点 const createMarker = (point) => { if (!useMap.AMap) return; const config = getMarkerConfig(point.type); if (!config) return message.info("未找到对应标点配置"); // 生成唯一标识符 const uuid = `${config.key}:${point[config.idKey]}`; const position = new AMap.LngLat(point.longitude, point.latitude); // 创建高德地图Marker实例 const marker = new AMap.Marker({ map: useMap.AMap, position, anchor: "bottom-center", zIndex: config.zIndex, extData: { item: { ...point, uuid }, data: config }, }); // 初始图标设置 updateMarkerIcon(marker, config, selectMarkerId.value === uuid); // 事件绑定 if (config.isOnLine(point)) { bindMarkerEvents(marker, config); } else { marker.setCursor("default"); } return marker; }; return { setMarkers, clearMarkers }; };3. 交互事件绑定
实现丰富的鼠标交互效果:
const bindMarkerEvents = (marker, config) => { const point = marker.getExtData(); const uuid = point.item?.uuid; // 鼠标移入事件 marker.on("mouseover", () => { marker.setTop(true); // 置顶显示 if (selectMarkerId.value !== uuid) { updateMarkerIcon(marker, config, true); // 切换为悬停图标 } // 显示文本标签 if (config.textKey && textLabel) { const text = point.item[config.textKey]; const position = marker.getPosition(); textLabel.show(); textLabel.setOffset(new AMap.Pixel(0, -55)); textLabel.setPosition(position); textLabel.setText(`<div class="amap-label-style">${text}</div>`); } }); // 鼠标移出事件 marker.on("mouseout", () => { marker.setTop(false); if (selectMarkerId.value !== uuid) { updateMarkerIcon(marker, config, false); // 恢复默认图标 } if (config.textKey && textLabel) { textLabel.hide(); // 隐藏文本标签 } }); // 点击事件 marker.on("click", () => { const prevUUID = selectMarkerId.value; selectMarkerId.value = uuid; // 更新前一个选中标记点的状态 if (prevUUID && prevUUID !== uuid) { const { marker, config } = getPrevMarkerConfig(prevUUID); if (marker && config) { updateMarkerIcon(marker, config, false); } } // 更新当前标记点状态 updateMarkerIcon(marker, config, true); // 计算信息框偏移量 let offset = new AMap.Pixel(0, -55); if (config.key === markerEnum.CLOSE) { offset = new AMap.Pixel(0, -25); } // 发送事件总线消息 eventBus.emit("marker-click", { type: point.item?.type, extData: { ...point, offset }, }); }); };4. 批量管理与状态控制
提供批量操作接口和状态管理:
// 设置标记点数据 const setMarkers = (points) => { // 清除旧标点 clearMarkers(); // 创建文本标签实例 textLabel = new AMap.Text({ map: useMap.AMap, anchor: "bottom-center", visible: false, zIndex: 30, }); // 批量创建新标记点 markers.value = points.map((point) => createMarker(point)); markerTypes.value = [...new Set(points.map((p) => p.type))]; }; // 清除所有标记点 const clearMarkers = () => { // 通知各类型组件关闭 markerTypes.value.forEach((type) => { eventBus.emit("marker-close", type); }); // 从地图移除所有标记点 markers.value.forEach((marker) => { marker?.setMap(null); }); // 清理文本标签 textLabel?.setMap(null); textLabel = null; markers.value = []; markerTypes.value = []; }; // 更新标记点图标 const updateMarkerIcon = (marker, config, isHover = false) => { const extData = marker.getExtData(); marker.setIcon(config.iconFilter(extData.item, isHover)); };5. 事件总线集成
通过事件总线实现跨组件通信:
// 监听外部关闭事件 eventBus.on("marker-close", (uuid) => { if (!uuid.includes("undefined")) { const { marker, config } = getPrevMarkerConfig(uuid); if (marker && config) updateMarkerIcon(marker, config, false); } }); // 获取前一个标记点配置 const getPrevMarkerConfig = (uuid) => { const prevMarker = markers.value.find( (m) => m.getExtData().item?.uuid === uuid ); const point = prevMarker?.getExtData(); return { marker: prevMarker, config: getMarkerConfig(point?.item?.type), }; };6. 在组件中使用
<template> <div class="map-container"> <div id="map" ref="mapRef"></div> </div> </template> <script setup> import { onMounted, ref } from 'vue'; import useMarker from '@/hooks/useMarker'; const mapRef = ref(null); const { setMarkers, clearMarkers } = useMarker(); // 模拟数据 const mockPoints = [ { type: 'SXJ', id: 1, name: '摄像头1', longitude: 116.397428, latitude: 39.90923, }, { type: 'ZBJL', id: 2, license: '京A12345', longitude: 116.407428, latitude: 39.91923, }, ]; onMounted(() => { // 初始化地图后设置标记点 setMarkers(mockPoints); }); // 组件卸载时清理 onUnmounted(() => { clearMarkers(); }); </script> <style scoped> .map-container { width: 100%; height: 600px; } .amap-label-style { background: white; padding: 4px 8px; border-radius: 4px; box-shadow: 0 2px 6px rgba(0, 0, 0, 0.15); font-size: 12px; white-space: nowrap; } </style>关键技术点
1. 配置驱动设计
通过中央化的配置对象,实现了标记点类型的灵活扩展。新增标记点类型只需在配置文件中添加相应配置即可。
2. 响应式状态管理
利用Vue3的ref和watch实现标记点状态的响应式管理,确保UI与数据状态同步。
3. 性能优化
使用事件委托减少事件绑定数量
按需创建和销毁标记点
利用高德地图的Marker层级控制
4. 组件通信
通过自定义事件总线实现标记点与业务组件的解耦通信。
总结
本文介绍了一个基于Vue3和高德地图的完整标记点管理系统。该系统具有以下特点:
高度可配置:通过配置文件管理所有标记点类型
丰富的交互:支持悬停、点击、选中等多种交互状态
良好的性能:优化了标记点的创建、更新和销毁机制
可扩展性:便于新增标记点类型和交互行为
代码复用:通过自定义Hook实现逻辑复用
这种设计模式不仅适用于地图应用,也可借鉴到其他需要管理大量可视化元素的场景中。通过合理的架构设计,可以使复杂的地图标记点管理变得简单而高效。
💰【我的自研工具推荐】轻松网购返利,技术人的省钱助手
✅ 自用省钱,分享有奖
支持主流电商平台 | 返利透明可提现
👇 微信扫码,立即体验
说明:本工具为我个人独立开发,若您有技术合作或使用疑问,欢迎在博客私信交流。