@loongbao-web-gis-utils/map-event-bus-openlayer
v1.0.7
Published
Map Event Bus OpenLayers 适配层
Downloads
509
Readme
使用手册
整体架构设计
MapEventBus 采用 Adapter(适配器)模式 设计,支持多种 GIS 框架。
┌─────────────────────────────────────┐
│ 业务模块 A / B / C │ ← 订阅 map:click 等业务事件
├─────────────────────────────────────┤
│ 接入适配器层(framework-specific)│ ← 注册原生事件 + 坐标转换
│ adapter-openlayer / adapter-xxx │
├─────────────────────────────────────┤
│ 核心层(framework-agnostic) │ ← EventBus 发布订阅
│ packages/core │
└─────────────────────────────────────┘- 核心层:框架无关的发布订阅事件总线(
EventBus),提供on/once/off/emit基础能力 - 适配器层:绑定特定 GIS 框架的原生事件,转换坐标,通过 EventBus 分发业务事件
- 业务层:订阅
map:click/map:dblclick/map:pointermove/map:moveend等业务事件
已有适配器
| 适配器包名 | GIS 框架 | 版本 |
|---|---|---|
| @loongbao-web-gis-utils/map-event-bus-openlayer | OpenLayers | v10.9.0 |
快速开始
安装
pnpm add @loongbao-web-gis-utils/map-event-bus-openlayer依赖 ol(peerDependency),需确保项目中已安装 ol >= 10.9.0。
基本用法
import OlMap from 'ol/Map.js'
import View from 'ol/View.js'
import TileLayer from 'ol/layer/Tile.js'
import XYZ from 'ol/source/XYZ.js'
import { fromLonLat } from 'ol/proj.js'
import { OpenLayersMapEventBus } from '@loongbao-web-gis-utils/map-event-bus-openlayer'
// 1. 创建地图
const map = new OlMap({
target: 'map',
layers: [
new TileLayer({
source: new XYZ({
url: 'https://webrd01.is.autonavi.com/appmaptile?lang=zh_cn&size=1&scale=1&style=8&x={x}&y={y}&z={z}',
}),
}),
],
view: new View({ center: fromLonLat([109.5, 19.1]), zoom: 9 }),
})
// 2. 创建 MapEventBus 实例(自动监听地图事件、禁用双击缩放)
const mapEventBus = new OpenLayersMapEventBus(map)
// 3. 获取 EventBus 并订阅业务事件
const bus = mapEventBus.getEventBus()
bus.on('map:click', ([lng, lat]) => {
console.log('点击位置:', lng, lat)
})
bus.on('map:dblclick', ([lng, lat]) => {
console.log('双击位置:', lng, lat)
})
bus.on('map:pointermove', ([lng, lat]) => {
console.log('鼠标位置:', lng, lat)
})
bus.on('map:moveend', (payload) => {
const { center, bbox, zoom } = payload as { center: number[]; bbox: number[]; zoom: number }
console.log('移动结束:', zoom, center, bbox)
})单次订阅
bus.once('map:click', ([lng, lat]) => {
console.log('仅首次点击:', lng, lat)
})取消订阅
const handler = ([lng, lat]: number[]) => { /* ... */ }
bus.on('map:click', handler)
bus.off('map:click', handler) // 取消特定回调
bus.off('map:click') // 取消所有 click 回调销毁
当不再需要时调用 destroy() 清理资源。会取消所有原生事件监听、清空 EventBus 订阅、恢复双击缩放交互。支持重复调用(幂等)。
mapEventBus.destroy()API 设计
类型定义
type Coordinate = [number, number] // [longitude, latitude]
type EventType = 'map:click' | 'map:dblclick' | 'map:pointermove' | 'map:moveend'
type EventHandler = (payload: Coordinate | MoveendPayload) => void
interface MoveendPayload {
bbox: [number, number, number, number] // [minLng, minLat, maxLng, maxLat]
center: [number, number] // [longitude, latitude]
zoom: number
}IEventBus 接口(核心层)
interface IEventBus {
on(type: string, handler: EventHandler): void
once(type: string, handler: EventHandler): void
off(type: string, handler?: EventHandler): void
emit(type: string, payload: Coordinate | MoveendPayload): void
}| 方法 | 说明 |
|---|---|
| on(type, handler) | 注册持久订阅 |
| once(type, handler) | 注册一次性订阅,首次触发后自动取消 |
| off(type, handler?) | 取消订阅;不传 handler 则取消该类型全部 |
| emit(type, payload) | 触发事件,所有回调收到 payload |
IMapEventBus 接口(适配器层)
interface IMapEventBus {
destroy(): void
getEventBus(): IEventBus
getCoord(e: unknown): number[]
getMoveendPayload(e: unknown): MoveendPayload
}| 方法 | 说明 |
|---|---|
| constructor(map) | 接收框架地图实例,注册原生事件 |
| getEventBus() | 返回内部 EventBus,供业务方订阅 |
| getCoord(e) | 从原生事件提取坐标,转换为 EPSG:4326 经纬度 |
| getMoveendPayload(e) | 从地图视图提取 moveend 数据(bbox/center/zoom) |
| destroy() | 取消所有监听,清空订阅,恢复交互(幂等) |
OpenLayersMapEventBus 类
import { OpenLayersMapEventBus } from '@loongbao-web-gis-utils/map-event-bus-openlayer'
const bus = new OpenLayersMapEventBus(map)constructor(map: OlMap)
创建实例,自动完成:
- 禁用
DoubleClickZoom交互(通过constructor.name定位) - 注册 click / dblclick / pointermove / moveend 原生事件
- click 带 300ms 防抖,双击不重复触发单击
getCoord(e: MapBrowserEvent<UIEvent>): number[]
返回 [longitude, latitude](EPSG:4326)
getMoveendPayload(e: MapEvent): MoveendPayload
返回值:
{
bbox: [minLng, minLat, maxLng, maxLat], // 视野范围(EPSG:4326)
center: [longitude, latitude], // 地图中心
zoom: 5 // 缩放级别
}destroy(): void
- 取消 map 上所有原生事件监听(click/dblclick/pointermove/moveend)
- 清除未执行的 click 防抖 timer
- 清空 EventBus 所有订阅
- 恢复 DoubleClickZoom 交互
在 Vue 3 中使用
<script setup lang="ts">
import { onMounted, onUnmounted } from 'vue'
import { OpenLayersMapEventBus } from '@loongbao-web-gis-utils/map-event-bus-openlayer'
let mapEventBus: OpenLayersMapEventBus | null = null
onMounted(() => {
mapEventBus = new OpenLayersMapEventBus(map)
const bus = mapEventBus.getEventBus()
bus.on('map:click', ([lng, lat]) => { /* 处理点击 */ })
bus.on('map:moveend', (payload) => {
const { zoom } = payload as { zoom: number }
/* 处理移动结束 */
})
})
onUnmounted(() => {
mapEventBus?.destroy()
})
</script>编写新适配器
以适配 Leaflet 为例:
1. 命名规范
| 项目 | 规范 |
|---|---|
| 包名 | @loongbao-web-gis-utils/map-event-bus-leaflet |
| 类名 | LeafletMapEventBus |
| 目录 | packages/adapter-leaflet/ |
2. 实现步骤
- 依赖
@loongbao-web-gis-utils/map-event-bus-core的EventBus和IMapEventBus - 实现
IMapEventBus接口 - 构造函数中绑定框架原生事件:
click、dblclick、pointermove、moveend getCoord(e)提取坐标转换为[lng, lat](EPSG:4326)getMoveendPayload(e)提取{ bbox, center, zoom }(EPSG:4326)destroy()中解绑事件并清理 EventBus(需幂等)- 分支覆盖率 >= 80%
3. 内部约束
- 业务事件名称:
map:click、map:dblclick、map:pointermove、map:moveend - 坐标格式统一
[longitude, latitude] - click 需实现 300ms 防抖,双击不触发单击
- dblclick 需阻止默认缩放行为(各框架实现不同)
注意事项
- 坐标格式:所有 emit 的坐标均为
[longitude, latitude](EPSG:4326) - 销毁时机:组件卸载时务必调用
destroy(),避免内存泄漏 - 幂等性:
destroy()可重复调用,不会抛出异常 - click 防抖:快速双击不会触发 click 事件,仅触发 dblclick
- 双击缩放:适配器自动禁用地图默认双击缩放,destroy 后恢复
