vue3-ol-map
v1.0.4
Published
A Vue3 + OpenLayers map helper library
Maintainers
Readme
📦 vue3-ol-map 地图辅助库
一个开箱即用、功能齐全、可高度配置的 Vue3 + OpenLayers 地图组件库。
支持多种底图、聚合点位、绘制测量、热力图、飞线轨迹、覆盖物动画与坐标转换。
本文按统一格式提供全部导出组件与 JS API 说明(UTF-8)。
📋 目录
✨ 特性
| 类别 | 能力 | 状态 | |---|---|---| | 地图控制 | 缩放/平移/旋转/范围限制/比例尺/缩放条/鹰眼/全屏 | ✅ | | 图层管理 | 图层增删改查、显隐切换、按 ID 清理、ZIndex 控制 | ✅ | | 覆盖物 | Overlay 增删查、闪烁点、CSS 扩散点 | ✅ | | 点位渲染 | 图标点、聚合点、海量点、编号点、文本点 | ✅ | | 绘制测量 | 点线面绘制、长度面积测量、结果回调 | ✅ | | 动画能力 | 轨迹动画、飞线流光、循环播放 | ✅ | | 坐标系 | EPSG:4326 / EPSG:3857 互转、投影自适应 | ✅ | | 工程能力 | composables 拆分、handle 风格 API、生命周期清理 | ✅ |
🛠 依赖及版本
{
"@turf/along": "^7.3.3",
"@turf/area": "^7.3.3",
"@turf/bbox-polygon": "^7.3.3",
"@turf/boolean-contains": "^7.3.3",
"@turf/buffer": "^7.3.3",
"@turf/kinks": "^7.3.3",
"@turf/length": "^7.3.3",
"@turf/line-intersect": "^7.3.3",
"lodash": "^4.17.23",
"ol": "^10.7.0",
"vue": "^3.4.0"
}📦 安装
npm i vue3-ol-map🚀 快速开始
import { createApp } from 'vue'
import App from './App.vue'
import Vue3OlMap from 'vue3-ol-map'
import 'vue3-ol-map/dist/style.css'
createApp(App).use(Vue3OlMap).mount('#app')<template>
<OpenLayers
ref="olRef"
:center="[87.611935, 43.963691]"
:zoom="11"
projection="EPSG:4326"
:mapType="4"
:layerMapDefault="layerMapDefault"
/>
</template>
<script setup>
const layerMapDefault =
'http://webrd02.is.autonavi.com/appmaptile?lang=zh_cn&size=1&scale=1&style=7&x={x}&y={y}&z={z}'
</script>🧩 详细使用示例
下面示例可直接粘贴到 App.vue 运行,包含:地图初始化、聚合、热力、绘制、测量、Popup、以及 JS handle 调用。
<template>
<div class="page">
<div class="toolbar">
<button @click="genCluster">生成聚合</button>
<button @click="clearCluster">清空聚合</button>
<button @click="genHeat">生成热力</button>
<button @click="clearHeat">清空热力</button>
<button @click="drawActive = !drawActive">{{ drawActive ? '停止绘制' : '开始绘制' }}</button>
<button @click="measureActive = !measureActive">{{ measureActive ? '停止测量' : '开始测量' }}</button>
<button @click="drawLineByJs">JS 画线</button>
<button @click="removeLineByJs">JS 删线</button>
</div>
<OpenLayers
ref="olRef"
class="map"
:center="center"
:zoom="11"
projection="EPSG:4326"
:mapType="4"
:layerMapDefault="layerMapDefault"
@ready="onReady"
@click="onMapClick"
>
<MapClusterLayer
id="demo-cluster"
name="demo-cluster"
:features="clusterFeatures"
@featureClick="onFeatureClick"
@clusterClick="onClusterClick"
/>
<MapHeat
name="demo-heat"
:data="heatData"
:radius="24"
:blur="30"
projection="EPSG:4326"
/>
<MapDraw
id="demo-draw"
name="demo-draw"
type="Polygon"
:actived="drawActive"
@drawend="onDrawEnd"
/>
<MapMeasureTool
id="demo-measure"
name="demo-measure"
type="LineString"
:actived="measureActive"
@mesureEnd="onMeasureEnd"
/>
<MapPopup
v-if="popup.show"
:position="popup.position"
:mapShow="popup.show"
title="点击坐标"
@close="popup.show = false"
>
<div>{{ popup.text }}</div>
</MapPopup>
</OpenLayers>
</div>
</template>
<script setup>
import { ref, reactive } from 'vue'
const olRef = ref(null)
const center = ref([87.611935, 43.963691])
const layerMapDefault =
'http://webrd02.is.autonavi.com/appmaptile?lang=zh_cn&size=1&scale=1&style=7&x={x}&y={y}&z={z}'
const clusterFeatures = ref([])
const heatData = ref([])
const drawActive = ref(false)
const measureActive = ref(false)
const lineHandle = ref(null)
const popup = reactive({
show: false,
position: [87.611935, 43.963691],
text: ''
})
const makePoints = (n = 200) =>
Array.from({ length: n }).map((_, i) => ({
geom: {
lon: 87.2 + Math.random() * 0.8,
lat: 43.5 + Math.random() * 0.5
},
attributes: { id: i + 1, name: `设备-${i + 1}` }
}))
const genCluster = () => {
clusterFeatures.value = makePoints(300)
}
const clearCluster = () => {
clusterFeatures.value = []
}
const genHeat = () => {
heatData.value = makePoints(220).map((it) => ({
lon: it.geom.lon,
lat: it.geom.lat,
weight: 0.2 + Math.random() * 0.8
}))
}
const clearHeat = () => {
heatData.value = []
}
const drawLineByJs = () => {
lineHandle.value = olRef.value?.createLine({
id: 'demo-line',
features: [
[87.55, 43.82],
[87.62, 43.86],
[87.68, 43.83]
],
projection: 'EPSG:4326',
lineStyle: { strokeColor: '#ff4d4f', strokeWeight: 4, showDir: true }
})
}
const removeLineByJs = () => {
lineHandle.value?.remove()
}
const onReady = () => {
genCluster()
genHeat()
}
const onMapClick = ({ lon, lat }) => {
popup.position = [lon, lat]
popup.text = `${lon.toFixed(6)}, ${lat.toFixed(6)}`
popup.show = true
}
const onFeatureClick = (feature) => console.log('featureClick', feature)
const onClusterClick = (features) => console.log('clusterClick size=', features.length)
const onDrawEnd = (result) => console.log('drawend', result)
const onMeasureEnd = (result) => console.log('mesureEnd', result)
</script>
<style scoped>
.page { width: 100%; height: 100vh; }
.toolbar { position: fixed; top: 10px; left: 10px; z-index: 1000; display: flex; gap: 8px; flex-wrap: wrap; }
.map { width: 100%; height: 100%; }
</style>📚 导出组件列表
- OpenLayers
- MapClusterLayer
- MapDraw
- MapPolygon
- MapIconMark
- MapOverlay
- MapPopup
- MapTrailAnimate
- MapHeat
- MapCircle
- MapLineString
- MapPointCollection
- MapServerClusterLayer
- MapVectorLayer
- MapMeasureTool
- MapFlyline
说明:
- 以上为
packages/index.js当前对外导出的全部组件。 MapTrackPlayer仓库中有实现,但未在packages/index.js导出。
🧱 组件文档
1. OpenLayers
中文描述:地图主容器组件,负责初始化地图、底图、控件和基础事件,是所有子组件的运行根节点。
使用示例:
<template>
<OpenLayers
ref="olRef"
:center="center"
:zoom="11"
projection="EPSG:4326"
:mapType="4"
:isOpenBottomZoom="true"
@ready="onReady"
@click="onMapClick"
/>
</template>
<script setup>
const center = [87.611935, 43.963691]
const onReady = ({ map }) => console.log('map ready', map)
const onMapClick = ({ lon, lat }) => console.log('click', lon, lat)
</script>Props 参数:
| 参数 | 类型 | 默认值 | 说明 |
|---|---|---|---|
| center | Array | [] | 地图中心点坐标 |
| layerMapDefault | String | '' | 自定义底图 URL 模板 |
| zoom | Number | 16 | 初始缩放 |
| minZoom | Number | 2 | 最小缩放 |
| maxZoom | Number | 28 | 最大缩放 |
| extent | Array | [] | 初始定位范围 |
| projection | String | 'EPSG:4326' | 地图坐标系 |
| rotation | Number | 0 | 旋转角度 |
| extentFlag | Boolean | true | 是否限制范围 |
| resolution | Array | [] | 分辨率数组 |
| isOpenScaleLine | Boolean | true | 是否显示比例尺 |
| isOpenZoomSlider | Boolean | true | 是否显示缩放滑块 |
| isOpenOverview | Boolean | true | 是否显示鹰眼 |
| isOpenFullScreen | Boolean | false | 是否显示全屏按钮 |
| isOpenBottomZoom | Boolean | false | 是否显示底部缩放值 |
| mapType | Number | 4 | 底图类型 |
| config | String/Object | {} | 业务扩展配置 |
| noLayer | Boolean | false | 是否不加载默认底图 |
Events 回调事件:
| 事件名 | 参数 | 说明 |
|---|---|---|
| ready | { map } | 地图初始化完成 |
| click | evt | 地图单击 |
| clickCluster | evt | 聚合点单击 |
| dblclick | evt | 地图双击 |
| rightClick | evt | 地图右键 |
| pointerMove | evt | 鼠标移动 |
| zoomMoveEnd | evt | 缩放结束 |
| zoomend | evt | 缩放结束(兼容事件) |
2. MapClusterLayer
中文描述:前端聚合图层组件,将离散点在当前缩放下聚合展示,并支持聚合点击回调。
使用示例:
<template>
<MapClusterLayer
id="device-cluster"
name="设备聚合"
:features="clusterFeatures"
:clusterDistance="60"
:zIndex="520"
fillColor="#2290e799"
fontColor="#fff"
@featureClick="onFeatureClick"
@clusterClick="onClusterClick"
/>
</template>
<script setup>
const clusterFeatures = [
{ geom: { lon: 87.61, lat: 43.96 }, attributes: { id: 1, name: '设备1' } },
{ geom: { lon: 87.62, lat: 43.95 }, attributes: { id: 2, name: '设备2' } }
]
const onFeatureClick = (feature) => console.log('单点点击', feature)
const onClusterClick = (list) => console.log('聚合点击数量', list.length)
</script>Props 参数:
| 参数 | 类型 | 默认值 | 说明 |
|---|---|---|---|
| id | String | - | 图层 ID(必填) |
| name | String | - | 图层名称(必填) |
| features | Array | [] | 聚合点数据 |
| visible | Boolean | true | 是否显示 |
| zIndex | Number | 6 | 图层层级 |
| clusterDistance | Number | 100 | 聚合距离 |
| fontColor | String | - | 聚合文字颜色 |
| fillColor | String | - | 聚合圆填充色 |
| img | String | - | 自定义图标 |
| scale | Number | 0.9 | 图标缩放 |
| offset | Array | [0,2] | 图标偏移 |
| clusterStyleFn | Function | - | 自定义聚合样式函数 |
| noClusterZoom | Number | 18 | 超过该缩放不聚合 |
| dataProjection | String | 'EPSG:4326' | 数据坐标系 |
Events 回调事件:
| 事件名 | 参数 | 说明 |
|---|---|---|
| featureClick | feature | 单点要素点击 |
| clusterClick | features[] | 聚合点点击 |
3. MapServerClusterLayer
中文描述:服务端聚合结果展示组件,适合后端已返回聚合后的点数据场景。
使用示例:
<template>
<MapServerClusterLayer
id="server-cluster"
name="服务端聚合"
:features="serverClusterFeatures"
:clusterDistance="30"
fillColor="#fea81a99"
fontColor="#fff"
/>
</template>
<script setup>
const serverClusterFeatures = [
{
geom: { lon: 87.58, lat: 43.91 },
attributes: { id: 100, pointType: 1, count: 36, name: '服务聚合点' }
}
]
</script>Props 参数:
| 参数 | 类型 | 默认值 | 说明 |
|---|---|---|---|
| id | String | - | 图层 ID(必填) |
| name | String | - | 图层名称(必填) |
| features | Array | [] | 聚合点数据 |
| visible | Boolean | true | 是否显示 |
| zIndex | Number | 6 | 图层层级 |
| clusterDistance | Number | 0 | 聚合距离 |
| fontColor | String | - | 聚合文字颜色 |
| fillColor | String | - | 聚合圆填充色 |
| img | String | - | 自定义图标 |
| scale | Number | 0.9 | 图标缩放 |
| offset | Array | [0,2] | 图标偏移 |
Events 回调事件:
| 事件名 | 参数 | 说明 | |---|---|---| | - | - | 无组件事件 |
4. MapPointCollection
中文描述:海量点位集合渲染组件,提供聚合视觉与点位分层显示能力。
使用示例:
<template>
<MapPointCollection
:pointList="pointList"
:distance="45"
:zIndex="500"
fillColor="#409eff"
fontColor="#fff"
@cluster-click="onPointClusterClick"
/>
</template>
<script setup>
const pointList = Array.from({ length: 1000 }).map((_, i) => ({
lon: 87.4 + Math.random() * 0.4,
lat: 43.7 + Math.random() * 0.3,
attributes: { id: i + 1 }
}))
const onPointClusterClick = (payload) => console.log('海量点聚合点击', payload)
</script>Props 参数:
| 参数 | 类型 | 默认值 | 说明 |
|---|---|---|---|
| pointList | Array | [] | 海量点数据 |
| distance | Number | 40 | 聚合距离 |
| offset | Array | [0,2] | 偏移 |
| fontColor | String | '#fff' | 聚合文字颜色 |
| fillColor | String | '#f00' | 聚合背景色 |
| zIndex | Number | 400 | 图层层级 |
| bgImg | String | - | 聚合背景图 |
| img | String | - | 点图标 |
| className | String | 'map-point-collection' | CSS 类名 |
| id | String | - | 图层 ID |
| scale | Number | 1 | 图标缩放 |
| elementName | String | 'el-mapPointCollection' | 元素标识名 |
Events 回调事件:
| 事件名 | 参数 | 说明 |
|---|---|---|
| cluster-click | payload | 聚合点点击 |
5. MapVectorLayer
中文描述:通用矢量要素图层组件,可渲染点/线/面等 Geo 要素。
使用示例:
<template>
<MapVectorLayer
id="vector-layer"
name="业务矢量图层"
:features="vectorFeatures"
:zIndex="530"
:visible="true"
/>
</template>
<script setup>
const vectorFeatures = [
{
geom: { lon: 87.60, lat: 43.95 },
attributes: { id: 1, name: '矢量点' },
symbol: { image: { icon: '/img/bluePoint.png', scale: 1 } }
}
]
</script>Props 参数:
| 参数 | 类型 | 默认值 | 说明 |
|---|---|---|---|
| id | String | - | 图层 ID(必填) |
| name | String | - | 图层名称(必填) |
| features | Array | [] | 矢量要素数组 |
| visible | Boolean | true | 是否显示 |
| zIndex | Number | 5 | 图层层级 |
Events 回调事件:
| 事件名 | 参数 | 说明 | |---|---|---| | - | - | 无组件事件 |
6. MapIconMark
中文描述:单点图标标注组件,支持图标与文本标签叠加显示。
使用示例:
<template>
<MapIconMark
:position="[87.6208, 43.8372]"
label="乌鲁木齐中心点"
:icon="iconUrl"
:zIndex="800"
/>
</template>
<script setup>
const iconUrl = '/img/bluePoint.png'
</script>Props 参数:
| 参数 | 类型 | 默认值 | 说明 |
|---|---|---|---|
| position | Array | [] | 点位坐标 |
| elementName | String | - | 元素标识名 |
| className | String | - | CSS 类名 |
| label | String | - | 文本标签 |
| icon | String | - | 图标地址 |
| zIndex | Number | 30 | 图层层级 |
Events 回调事件:
| 事件名 | 参数 | 说明 | |---|---|---| | - | - | 无组件事件 |
7. MapLineString
中文描述:折线渲染组件,用于轨迹线、路径线等线要素展示。
使用示例:
<template>
<MapLineString
:pointList="linePoints"
lineColor="#ff4d4f"
:lineWidth="3"
:lineDash="[8, 6]"
:zIndex="300"
/>
</template>
<script setup>
const linePoints = [
[87.57, 43.81],
[87.60, 43.84],
[87.64, 43.86]
]
</script>Props 参数:
| 参数 | 类型 | 默认值 | 说明 |
|---|---|---|---|
| pointList | Array | [] | 折线坐标点 |
| elementName | String | '' | 元素标识名 |
| className | String | 'map-line-string' | CSS 类名 |
| lineColor | String | '#409eff' | 线颜色 |
| lineWidth | Number | 2 | 线宽 |
| lineDash | Array | [] | 虚线配置 |
| zIndex | Number | 300 | 图层层级 |
Events 回调事件:
| 事件名 | 参数 | 说明 | |---|---|---| | - | - | 无组件事件 |
8. MapPolygon
中文描述:多边形渲染组件,用于区域范围、围栏等面要素展示。
使用示例:
<template>
<MapPolygon
:pointList="polygonPoints"
fillColor="rgba(64,158,255,0.2)"
lineColor="#409eff"
:lineWidth="2"
:zIndex="260"
/>
</template>
<script setup>
const polygonPoints = [
[87.56, 43.90],
[87.63, 43.90],
[87.64, 43.95],
[87.57, 43.96],
[87.56, 43.90]
]
</script>Props 参数:
| 参数 | 类型 | 默认值 | 说明 |
|---|---|---|---|
| pointList | Array | [] | 面坐标点 |
| elementName | String | '' | 元素标识名 |
| className | String | 'map-polygon' | CSS 类名 |
| lineColor | String | '#409eff' | 边线颜色 |
| lineWidth | Number | 2 | 边线宽度 |
| lineDash | Array | [] | 虚线配置 |
| fillColor | String | 'rgba(0,0,0,0.8)' | 填充色 |
| zIndex | Number | 200 | 图层层级 |
Events 回调事件:
| 事件名 | 参数 | 说明 | |---|---|---| | - | - | 无组件事件 |
9. MapCircle
中文描述:圆形渲染组件,支持按中心点和半径绘制圆范围。
使用示例:
<template>
<MapCircle
:position="circleCenter"
:radius="1200"
fillColor="rgba(255,0,0,0.15)"
lineColor="#ff4d4f"
:lineWidth="2"
/>
</template>
<script setup>
const circleCenter = [87.611935, 43.963691]
</script>Props 参数:
| 参数 | 类型 | 默认值 | 说明 |
|---|---|---|---|
| position | Array | [] | 圆心坐标 |
| radius | Number | 100 | 半径 |
| elementName | String | '' | 元素标识名 |
| className | String | 'map-circle' | CSS 类名 |
| lineColor | String | '#409eff' | 边线颜色 |
| lineWidth | Number | 2 | 边线宽度 |
| lineDash | Array | [] | 虚线配置 |
| fillColor | String | 'rgba(255,255,255,0.5)' | 填充色 |
| zIndex | Number | 200 | 图层层级 |
Events 回调事件:
| 事件名 | 参数 | 说明 | |---|---|---| | - | - | 无组件事件 |
10. MapHeat
中文描述:热力图组件,根据点位密度与权重渲染热点强度分布。
使用示例:
<template>
<MapHeat
name="risk-heat"
:data="heatData"
:radius="24"
:blur="30"
:gradient="['#2e7d32', '#ffeb3b', '#ff9800', '#f44336']"
projection="EPSG:4326"
/>
</template>
<script setup>
const heatData = [
{ lon: 87.62, lat: 43.84, weight: 0.2 },
{ lon: 87.61, lat: 43.85, weight: 0.8 },
{ lon: 87.60, lat: 43.83, weight: 0.5 }
]
</script>Props 参数:
| 参数 | 类型 | 默认值 | 说明 |
|---|---|---|---|
| name | String | 'heatLayer' | 图层名称 |
| data | Array | [] | 热力点数据 |
| gradient | Array | ['#00f','#0ff','#0f0','#ff0','#f00'] | 颜色梯度 |
| radius | Number/String | 16 | 热力半径 |
| blur | Number/String | 25 | 模糊半径 |
| zIndex | Number/String | 1 | 图层层级 |
| longitude | String | 'lon' | 经度字段名 |
| latitude | String | 'lat' | 纬度字段名 |
| visible | Boolean | true | 是否显示 |
| weight | String | 'weight' | 权重字段名 |
| projection | String | 'EPSG:4326' | 数据坐标系 |
Events 回调事件:
| 事件名 | 参数 | 说明 |
|---|---|---|
| update:radius | radius | 半径变更 |
11. MapOverlay
中文描述:通用覆盖物组件,可在地图坐标位置挂载任意自定义 DOM 内容。
使用示例:
<template>
<MapOverlay :position="position" :offset="[0, -20]">
<div class="overlay-card">
<div class="title">站点 A-01</div>
<div class="desc">在线 · 温度 26℃</div>
</div>
</MapOverlay>
</template>
<script setup>
const position = [87.6, 43.8]
</script>Props 参数:
| 参数 | 类型 | 默认值 | 说明 |
|---|---|---|---|
| position | Array | [] | 覆盖物坐标 |
| className | String | 'map-overlay' | CSS 类名 |
| offset | Array | [0,0] | 偏移量 |
Events 回调事件:
| 事件名 | 参数 | 说明 | |---|---|---| | - | - | 无组件事件 |
12. MapPopup
中文描述:地图弹窗组件,内置标题与关闭逻辑,适合信息提示与详情展示。
使用示例:
<template>
<MapPopup
:position="popup.position"
:mapShow="popup.show"
title="设备详情"
:offset="[0, 8]"
@close="popup.show = false"
@update:mapShow="popup.show = $event"
>
<div>设备编号:{{ popup.deviceId }}</div>
<div>状态:{{ popup.status }}</div>
</MapPopup>
</template>
<script setup>
import { reactive } from 'vue'
const popup = reactive({
show: true,
position: [87.621, 43.827],
deviceId: 'D-1024',
status: '在线'
})
</script>Props 参数:
| 参数 | 类型 | 默认值 | 说明 |
|---|---|---|---|
| position | Array | [] | 弹窗坐标 |
| className | String | 'map-popup' | CSS 类名 |
| mapShow | Boolean | true | 是否显示 |
| title | String | '' | 标题 |
| offset | Array | [0,0] | 偏移量 |
| bodyStyle | Object | {} | body 样式 |
| showClose | Boolean | true | 是否显示关闭按钮 |
Events 回调事件:
| 事件名 | 参数 | 说明 |
|---|---|---|
| close | false | 点击关闭 |
| update:mapShow | false | v-model 同步显示状态 |
13. MapDraw
中文描述:绘制交互组件,支持点线面及圆/矩形等图形绘制,并输出结果数据。
使用示例:
<template>
<MapDraw
id="draw-layer"
name="绘制图层"
type="Polygon"
:actived="drawActive"
:drawStyle="drawStyle"
@drawend="onDrawEnd"
@polygonCenter="onPolygonCenter"
/>
</template>
<script setup>
import { ref } from 'vue'
const drawActive = ref(true)
const drawStyle = { stroke: { color: '#409eff' }, fill: { color: 'rgba(64,158,255,0.2)' } }
const onDrawEnd = (result) => console.log('drawend', result)
const onPolygonCenter = (center) => console.log('polygon center', center)
</script>Props 参数:
| 参数 | 类型 | 默认值 | 说明 |
|---|---|---|---|
| type | String | 'Polygon' | 绘制类型 |
| id | String | - | 交互 ID |
| name | String | - | 交互名称 |
| actived | Boolean | false | 是否激活绘制 |
| poiMap | Object | - | 业务传入对象 |
| typeFlag | Boolean | - | 类型切换标记 |
| drawStyle | Object | SymbolConfig.defaultDrawStyle | 绘制样式 |
Events 回调事件:
| 事件名 | 参数 | 说明 |
|---|---|---|
| drawend | result | 绘制结束结果 |
| radiusAndCenter | { centerLogLat, radius1 } | 圆形中心与半径 |
| drawTwoPoints | points[] | 绘圆推导出的两点 |
| boxCenter | center | 矩形中心点 |
| polygonCenter | center | 面中心点 |
| getPolygonArea | area | 面积结果 |
| mapVueComponent | mapVm | 地图组件实例 |
| error | error | 异常信息 |
14. MapMeasureTool
中文描述:测量交互组件,支持长度与面积测量并可视化显示结果标签。
使用示例:
<template>
<MapMeasureTool
id="measure-layer"
name="测量工具"
:actived="measureActive"
type="LineString"
:isClear="measureClear"
@mesureEnd="onMeasureEnd"
@update:isClear="measureClear = $event"
/>
</template>
<script setup>
import { ref } from 'vue'
const measureActive = ref(true)
const measureClear = ref(false)
const onMeasureEnd = (result) => console.log('measure result', result)
</script>Props 参数:
| 参数 | 类型 | 默认值 | 说明 |
|---|---|---|---|
| type | String | 'LineString' | 测量类型(线/面) |
| id | String | 'MapMeasureTool' | 交互 ID |
| name | String | 'MapMeasureTool' | 交互名称 |
| actived | Boolean | false | 是否激活测量 |
| isClear | Boolean | false | 外部触发清理 |
| drawStyle | Object | SymbolConfig.defaultMeasureStyle | 测量样式 |
Events 回调事件:
| 事件名 | 参数 | 说明 |
|---|---|---|
| mesureEnd | result | 测量结束数据 |
| update:isClear | false | 清理后重置状态 |
15. MapTrailAnimate
中文描述:轨迹动画组件,支持轨迹播放、暂停、循环及位置更新事件。
使用示例:
<template>
<MapTrailAnimate
ref="trailRef"
:features="trailPoints"
:speed="14"
:loop="true"
:lineStyle="{ strokeColor: '#409eff', strokeWeight: 4 }"
@positionUpdate="onMove"
@animationEnd="onTrailEnd"
/>
</template>
<script setup>
const trailPoints = [
[87.57, 43.81],
[87.60, 43.83],
[87.62, 43.86]
]
const onMove = (payload) => console.log('positionUpdate', payload)
const onTrailEnd = () => console.log('trail end')
</script>Props 参数:
| 参数 | 类型 | 默认值 | 说明 |
|---|---|---|---|
| features | Array | [] | 轨迹点数组 |
| dataProjection | String | 'EPSG:4326' | 数据坐标系 |
| startPic | String | defaultImage.startPic | 起点图标 |
| endPic | String | defaultImage.endPic | 终点图标 |
| carPic | String | defaultImage.carPic | 运动图标 |
| loop | Boolean | false | 是否循环 |
| speed | Number | 10 | 运动速度 |
| lineStyle | Object | {} | 轨迹线样式 |
| attributes | Object | {} | 附加属性 |
Events 回调事件:
| 事件名 | 参数 | 说明 |
|---|---|---|
| start | - | 开始播放 |
| paused | - | 暂停 |
| resumed | - | 恢复 |
| restarted | - | 重播 |
| followToggled | follow | 跟随状态切换 |
| turnPoint | { index } | 到达拐点 |
| positionUpdate | payload | 位置实时更新 |
| animationStart | - | 动画开始 |
| animationEnd | - | 动画结束 |
| loopStart | - | 循环开始 |
| error | error | 异常信息 |
16. MapFlyline
中文描述:飞线动画组件,支持起终点、粒子流光和交互点击,用于迁徙/流向可视化。
使用示例:
<template>
<MapFlyline
id="flyline-1"
name="全国流向"
:flights="flights"
:lineWidth="2"
lineColor="#00ffff"
:glow="true"
:animationDuration="3000"
:loop="true"
@flightClick="onFlightClick"
/>
</template>
<script setup>
const flights = [
{
id: 'f1',
from: { lon: 87.61, lat: 43.96 },
to: { lon: 121.47, lat: 31.23 },
curve: 0.3,
value: 80
}
]
const onFlightClick = (e) => console.log('flightClick', e)
</script>Props 参数:
| 参数 | 类型 | 默认值 | 说明 |
|---|---|---|---|
| id | String | - | 图层 ID(必填) |
| name | String | - | 图层名称(必填) |
| flights | Array | [] | 飞线数据 |
| visible | Boolean | true | 是否显示 |
| zIndex | Number | 10 | 图层层级 |
| lineColor | String | '#00ffff' | 飞线颜色 |
| lineWidth | Number | 2 | 飞线宽度 |
| lineOpacity | Number | 0.8 | 飞线透明度 |
| particleColor | String | '#ffffff' | 粒子颜色 |
| particleSize | Number | 4 | 粒子大小 |
| speed | Number | 5 | 粒子速度 |
| showStartPoint | Boolean | true | 显示起点 |
| showEndPoint | Boolean | true | 显示终点 |
| startPointStyle | Object | { color:'#00ff00', size:6 } | 起点样式 |
| endPointStyle | Object | { color:'#ff0000', size:6 } | 终点样式 |
| animationDuration | Number | 3000 | 动画时长(ms) |
| loop | Boolean | true | 是否循环 |
| density | Number | 1 | 密度过滤(0-1) |
| glow | Boolean | true | 是否启用流光 |
| glowWidth | Number | 6 | 流光宽度 |
| glowColor | String | '#52e884' | 流光颜色 |
| glowSpeed | Number | 0.02 | 流光速度 |
| glowTrailLength | Number | 0.3 | 流光尾迹长度 |
| dataProjection | String | 'EPSG:4326' | 数据坐标系 |
Events 回调事件:
| 事件名 | 参数 | 说明 |
|---|---|---|
| flightClick | { feature, flightData, index, coordinate } | 点击飞线要素 |
🧠 OpenLayers Composables(use 方法)
中文描述:packages/OpenLayers/composables 将 index.vue 的“大而全方法”拆分为核心、图层、覆盖物、绘制四类 API;组件层只负责渲染和生命周期。
useMapCore
职责:地图初始化、基础事件总线、视图控制、控件开关、坐标转换与销毁清理。
常用方法:
| 方法 | 入参 | 返回值 | 说明 |
|---|---|---|---|
| initMap | () | void | 初始化地图与默认底图,触发 ready |
| getMap | () | Map | 获取地图实例 |
| getView | () | View | 获取视图实例 |
| centerAndZoom | (x, y, level) | void | 设置中心和缩放 |
| setCenter | (point) | void | 设置中心点 |
| setLevel | (level) | void | 设置缩放级别 |
| getCenter / getLevel | () | Array / Number | 获取中心点与缩放级别 |
| fitToExtent | (extent, options?) | void | 按范围定位 |
| fitToLayer | (layerId, options?) | void | 按图层范围定位 |
| flyTo | ({ center, zoom, duration, dataProjection }, done?) | void | 飞行动画定位 |
| transformCoord | (coord, sourceProjection?, targetProjection?) | coord | 坐标转换 |
| setRotation / getRotation | (rotation) / () | void / Number | 设置/获取旋转角 |
| setZoomSliderEnabled | (enabled) | void | 开关缩放滑块控件 |
| setScaleLineEnabled | (enabled) | void | 开关比例尺控件 |
| setFullScreenEnabled | (enabled) | void | 开关全屏控件 |
| setOverviewEnabled | (enabled) | void | 开关鹰眼控件 |
| $on / $off / $once | (event, handler) | void | 组件内事件总线 |
| disposeMapCore | () | void | 释放地图、事件与控件资源 |
useMapLayerApi
职责:图层管理、底图切换、聚合模板、批量点标注,以及整合 useCreateLine、useCreateIconMark。
常用方法:
| 方法 | 入参 | 返回值 | 说明 |
|---|---|---|---|
| getAllLayers | () | Array | 获取所有图层 |
| getLayer | (layerId) | Layer \\| null | 按 ID 获取图层 |
| addLayer | (config) | Layer | 新建矢量/聚合图层 |
| removeLayer / removeLayerById | (id) | void | 删除图层 |
| clearLayerFeatures | (id) | void | 清空图层要素 |
| isLayerExist | (id) | Boolean | 判断图层是否存在 |
| switchBaseMap | (config) | void | 切换底图 |
| createClusterTemplate | (config) | handle | 创建聚合模板图层(handle 版) |
| addPointsWithNumbers | (config) | handle \\| null | 批量点+编号渲染 |
| wGS84ToMercator | (point) | point | WGS84 转墨卡托 |
| mercatorToWGS84 | (point) | point | 墨卡托转 WGS84 |
useMapOverlayApi
职责:Overlay 管理与闪烁点动画 Overlay 能力。
常用方法:
| 方法 | 入参 | 返回值 | 说明 |
|---|---|---|---|
| getOverlays | () | Collection | 获取所有 overlay |
| addOverlay | (overlay) | void | 添加 overlay |
| removeOverlay | (overlay) | void | 移除 overlay |
| getOverlayById | (id) | Overlay \\| null | 按 ID 获取 overlay |
| deleteOverlayById | (id) | void | 按 ID 删除 overlay |
| setFlashPoint | (id, point, color, attributes) | handle | 添加橙色雷达扩散点 |
| setFlashPoint2 | (id, point, color, attributes) | handle | 添加 CSS 扩散点 |
useMapDrawApi
职责:交互管理入口,整合 useDrawTools 并提供 interaction 的增删查。
常用方法:
| 方法 | 入参 | 返回值 | 说明 |
|---|---|---|---|
| addInteraction | (interaction, isActive?) | void | 添加交互并设置激活状态 |
| removeInteraction | (interaction) | void | 删除交互 |
| getInteractions | () | Collection | 获取所有交互 |
useCreateLine
职责:线/面图层快速创建 API(handle 版)。
常用方法:
| 方法 | 入参 | 返回值 | 说明 |
|---|---|---|---|
| createLine | (config) | handle \\| null | 创建线图层,支持箭头样式 |
| createMapPolygon | (config) | handle \\| null | 创建单个面图层 |
| createMapPolygons | (polygons, opts?) | handle | 批量创建面图层 |
| removeLineLayerById | (id) | void | 删除指定线/面图层 |
| disposeCreateLine | () | void | 清理内部创建的图层 |
使用示例(createLine / createMapPolygon / createMapPolygons):
<template>
<OpenLayers ref="olRef" :center="[87.611935, 43.963691]" :zoom="11" projection="EPSG:4326" />
<div class="ops">
<button @click="drawLine">画线</button>
<button @click="updateLine">更新线</button>
<button @click="removeLine">删线</button>
<button @click="drawPolygon">画单面</button>
<button @click="drawPolygons">画批量面</button>
</div>
</template>
<script setup>
import { ref } from 'vue'
const olRef = ref(null)
const lineHandle = ref(null)
const polygonHandle = ref(null)
const polygonsHandle = ref(null)
const linePoints = [
[87.57, 43.81],
[87.60, 43.84],
[87.64, 43.86]
]
const drawLine = () => {
lineHandle.value = olRef.value?.createLine({
id: 'line-demo',
features: linePoints,
projection: 'EPSG:4326',
lineStyle: { strokeColor: '#ff4d4f', strokeWeight: 4, showDir: true }
})
}
const updateLine = () => {
lineHandle.value?.update({
features: [
[87.57, 43.81],
[87.62, 43.85],
[87.66, 43.83]
]
})
}
const removeLine = () => lineHandle.value?.remove()
const drawPolygon = () => {
polygonHandle.value = olRef.value?.createMapPolygon({
id: 'polygon-single',
pointList: [
[87.56, 43.90],
[87.63, 43.90],
[87.64, 43.95],
[87.56, 43.90]
],
fillColor: 'rgba(64,158,255,0.2)',
lineColor: '#409eff'
})
}
const drawPolygons = () => {
polygonsHandle.value = olRef.value?.createMapPolygons(
[
{ id: 'a1', pointList: [[87.5, 43.8], [87.55, 43.8], [87.55, 43.85], [87.5, 43.8]], fillColor: 'rgba(255,0,0,.2)' },
{ id: 'a2', pointList: [[87.65, 43.9], [87.7, 43.9], [87.7, 43.95], [87.65, 43.9]], fillColor: 'rgba(0,128,255,.2)' }
],
{ layerKey: 'batch-polygons', zIndex: 300 }
)
}
</script>useCreateIconMark
职责:点标注/行政区/GeoJSON 图层快速创建 API(handle 版)。
常用方法:
| 方法 | 入参 | 返回值 | 说明 |
|---|---|---|---|
| createIconMark | (config) | handle \\| null | 创建单点图标标注 |
| districtQuery | (geojson) | handle \\| null | 渲染行政区 GeoJSON |
| addGeoJson | (geojson, styleOpts?) | handle \\| null | 添加 GeoJSON 图层并定位 |
| removeGeoJsonLayer | () | void | 删除默认 GeoJSON 图层 |
| removeIconLayerById | (id) | void | 删除指定标注图层 |
| disposeCreateIconMark | () | void | 清理内部创建图层 |
使用示例(createIconMark / districtQuery / addGeoJson):
<template>
<OpenLayers ref="olRef" :center="[87.611935, 43.963691]" :zoom="11" projection="EPSG:4326" />
<div class="ops">
<button @click="addIconMark">加点标注</button>
<button @click="addDistrict">行政区</button>
<button @click="addBoundary">GeoJSON 边界</button>
<button @click="clearGeo">清除边界</button>
</div>
</template>
<script setup>
import { ref } from 'vue'
const olRef = ref(null)
const iconHandle = ref(null)
const districtHandle = ref(null)
const geoHandle = ref(null)
const addIconMark = () => {
iconHandle.value = olRef.value?.createIconMark({
id: 'mark-001',
position: [87.62, 43.84],
projection: 'EPSG:4326',
icon: '/img/bluePoint.png',
label: '设备 A-01',
attributes: { id: 1, type: 'device', status: 'online' },
style: { color: '#fff', bgColor: 'rgba(0,0,0,0.6)' }
})
}
const districtGeoJson = {
type: 'FeatureCollection',
features: [
{
type: 'Feature',
geometry: { type: 'Polygon', coordinates: [[[87.5, 43.8], [87.7, 43.8], [87.7, 44.0], [87.5, 44.0], [87.5, 43.8]]] },
properties: { name: '示例行政区' }
}
]
}
const addDistrict = () => {
districtHandle.value = olRef.value?.districtQuery(districtGeoJson)
}
const addBoundary = () => {
geoHandle.value = olRef.value?.addGeoJson(districtGeoJson, {
id: 'boundary-001',
color: '#62aad0',
width: 2,
colorFill: 'rgba(98,170,208,0.15)'
})
}
const clearGeo = () => olRef.value?.removeGeoJsonLayer()
</script>useDrawTools
职责:绘制辅助 API,包括圆形绘制、文本标注和批量图标标注。
常用方法:
| 方法 | 入参 | 返回值 | 说明 |
|---|---|---|---|
| drawCircle | (options) | handle \\| null | 绘制圆形范围 |
| addTextLabels | (cfg) | handle \\| null | 添加可交互文本标签图层 |
| setMapIconMarks | (list, opts?) | handle \\| null | 批量增量更新图标点标注 |
使用示例(drawCircle / addTextLabels / setMapIconMarks):
<template>
<OpenLayers ref="olRef" :center="[87.611935, 43.963691]" :zoom="11" projection="EPSG:4326" />
<div class="ops">
<button @click="drawCircleByJs">画圆</button>
<button @click="addLabels">加文本</button>
<button @click="addIconMarks">加图标组</button>
</div>
</template>
<script setup>
import { ref } from 'vue'
const olRef = ref(null)
const circleHandle = ref(null)
const labelHandle = ref(null)
const drawCircleByJs = () => {
circleHandle.value = olRef.value?.drawCircle({
id: 'circle-js',
position: [87.6119, 43.9636],
radius: 1000,
dataProjection: 'EPSG:4326',
lineColor: '#ff4d4f',
fillColor: 'rgba(255,77,79,0.2)',
attributes: { zone: '告警区' }
})
}
const addLabels = () => {
labelHandle.value = olRef.value?.addTextLabels({
layerId: 'text-label-layer',
data: [
{ text: '站点A', coord: [87.60, 43.86], attributes: { id: 1 } },
{ text: '站点B', coord: [87.63, 43.84], attributes: { id: 2 } }
],
dataProjection: 'EPSG:4326',
style: { font: 'bold 14px Arial', fillColor: '#ff0000' },
onClick: (feature, attrs) => console.log('label click', attrs),
onHover: (feature, attrs) => console.log('label hover', attrs),
onMouseOut: (feature, attrs) => console.log('label out', attrs),
clearBefore: true
})
}
const addIconMarks = () => {
olRef.value?.setMapIconMarks(
[
{ id: 'p1', position: [87.58, 43.82], label: 'P1', icon: '/img/bluePoint.png', attributes: { level: 1 } },
{ id: 'p2', position: [87.62, 43.88], label: 'P2', icon: '/img/bluePoint.png', attributes: { level: 2 } }
],
{ layerKey: 'icon-marks', zIndex: 50, dataProjection: 'EPSG:4326' }
)
}
</script>🔁 JS API(旧调用 -> handle 新写法)
// 旧调用(兼容)
this.$refs.olmap.createLine({ id: 'line-1', features: points })
// 推荐新写法(handle)
const lineHandle = this.$refs.olmap.createLine({ id: 'line-1', features: points })
lineHandle?.update({ features: nextPoints })
lineHandle?.remove() // 或 dispose()更多对照见 USAGE.md。
🧰 非组件导出
import { GeometryType, GeometryUtil, Util } from 'vue3-ol-map'
console.log(GeometryType.POLYGON)🖥 示例页面
- 综合演示:
/mapDemo - 组合式分组演示:
/compositionDemo - JS API 验证页:
/jsApiDemo
💻 本地开发
npm install
npm run dev
npm run build❓ 常见问题
1. 地图不显示
- 检查容器是否有明确宽高(如
height: 100vh)。 - 检查底图 URL 是否可访问。
- 检查
center与projection是否匹配。
2. Heat 没有效果
- 确认数据字段与 props 一致(默认
lon/lat/weight)。 - 确认
MapHeat在OpenLayers子节点内。 - 建议先用
projection="EPSG:4326"与经纬度数据验证。
3. 为什么 npm run build 报 terser not found
- 当前工程默认构建会走 terser 压缩,若本地缺依赖会报错。
- 可先使用:
npx vite build --minify=false验证构建流程。
4. 如何切换底图
this.$refs.olRef.switchBaseMap({
type: 4,
url: 'http://webrd02.is.autonavi.com/appmaptile?lang=zh_cn&size=1&scale=1&style=7&x={x}&y={y}&z={z}'
})📝 更新日志
v1.0.2(当前)
- Vue3 + OpenLayers 组件库发布版本。
OpenLayers/index.vue逻辑拆分到 composables:useMapCore / useMapLayerApi / useMapOverlayApi / useMapDrawApi。- createLine/createIconMark/drawMixins 提供 composable 化和 handle 风格调用。
- 增补完整 README:全组件参数表、事件表、use 方法说明。
🤝 贡献指南
- Fork 本仓库。
- 新建分支:
git checkout -b codex/feature-name。 - 提交修改:
git commit -m "feat: xxx"。 - 推送分支并发起 PR。
📄 许可证
MIT(见 package.json 中 license 字段)。
