npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

vue3-ol-map

v1.0.4

Published

A Vue3 + OpenLayers map helper library

Readme

📦 vue3-ol-map 地图辅助库

npm version npm downloads license javascript Vue

一个开箱即用、功能齐全、可高度配置的 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/composablesindex.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

职责:图层管理、底图切换、聚合模板、批量点标注,以及整合 useCreateLineuseCreateIconMark

常用方法:

| 方法 | 入参 | 返回值 | 说明 | |---|---|---|---| | 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 是否可访问。
  • 检查 centerprojection 是否匹配。

2. Heat 没有效果

  • 确认数据字段与 props 一致(默认 lon/lat/weight)。
  • 确认 MapHeatOpenLayers 子节点内。
  • 建议先用 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 方法说明。

🤝 贡献指南

  1. Fork 本仓库。
  2. 新建分支:git checkout -b codex/feature-name
  3. 提交修改:git commit -m "feat: xxx"
  4. 推送分支并发起 PR。

📄 许可证

MIT(见 package.jsonlicense 字段)。