vue3+openlayes实现离线地图加载
概述
OpenLayers 使在任何网页中放置动态地图变得容易。它可以显示从任何来源加载的地图图块、矢量数据和标记。OpenLayers 的开发旨在进一步使用各种地理信息。它是完全免费的开源 JavaScript
官网
https://openlayers.org/
1、下载依赖
npm i ol -S
2、下载瓦片
这里使用 全能地图下载工具 也可使用别的下载工具,可以将瓦片下下来就行
https://pan.baidu.com/s/1LzFMrxHpdQpGp1W5JZiU6Ad7tg
由于瓦片太大,放在项目里不合适,这里将瓦片放在 nginx 里了,也可放在项目里测,不建议,加载会很慢
vue代码
<template> <div id="map" /> <div id="popup" ref="mapContent" v-html="mapText"></div> </template> <script setup> import 'ol/ol.css' import TileLayer from 'ol/layer/Tile' import XYZ from 'ol/source/XYZ' import VectorLayer from 'ol/layer/Vector' import VectorSource from 'ol/source/Vector' import { Map, View, Feature, Overlay } from 'ol' import { Point as olPoint } from 'ol/geom' import { fromLonLat, transform } from 'ol/proj' import { Style, Fill, Icon, Text } from 'ol/style' import { onMounted, reactive, ref } from 'vue' const mapView = reactive({ center: fromLonLat([119.02888, 33.54414]), // 地图中心点 zoom: 11, // 初始缩放级别 minZoom: 8, // 最小缩放级别 maxZoom: 16 // 最大缩放级别 }) let map = ref(null) // 弹框 const overlay = ref(null) const mapContent = ref(null) const mapText = ref(null) // 初始化地图 const init = () => { const mapUrl = ref( import.meta.env.VITE_MAP_URL + '/{z}/{x}/{y}.png') console.log(mapUrl.value) const tileLayer = new TileLayer({ source: new XYZ({ url: mapUrl.value }) }) map.value = new Map({ layers: [tileLayer], view: new View(mapView), target: 'map' }) // 创建点位数组 const peoples = [{ name: '张三', title: '化工大院', long: [119.01819, 33.55102] }, { name: '李四', title: '东仪小区', long: [119.04211, 33.53684] }, { name: '王五', title: '紫郡长安', long: [119.01738, 33.58877] } ] // 循环将每个人都添加在地图上 peoples.forEach((v) => { addLayer(v) }) // 初始化地图之后就将弹框挂载好,后续只是修改显示的位置 createOverlay() // 地图点击 map.value.on('click', (e) => mapClick(e)) } // 添加点位 const addLayer = (v) => { const layer = new VectorLayer({ source: new VectorSource() }) // 添加图层 map.value.addLayer(layer) // 创建 feature 坐标信息 const feature = new Feature({ // 经纬度转换成坐标信息 geometry: new olPoint(fromLonLat(v.long)), // 可以带别的参数,key 可以随便写,不冲突就行,这里将所有的参数都放进来,供后续使用 ...v }) feature.setStyle( new Style({ // 标点的图片,如果要标不同类型的点,这个图片可以判断加 image: new Icon({ crossOrigin: 'anonymous', src: new URL('../assets/img/people_mark1.png', import.meta.url).href }), // 标点的文字 text: new Text({ // 文字 text: v.name, // 文字样式 fill: new Fill({ color: 'red' }), font: '20px Calibri', // 偏移量 offsetY: 20 }) }) ) // 将 feature 坐标信息添加在地图上 layer.getSource().addFeatures([feature]) } // 创建弹框 const createOverlay = () => { overlay.value = new Overlay({ element: mapContent.value, // 将弹框挂载在 dom 上 autoPan: true, // 如果弹框显示不全则自动归位 positioning: 'bottom-center', // 相对于其位置属性的实际位置 stopEvent: true, // 事件冒泡 autoPanAnimation: { duration: 300 // 地图移动速度 } }) map.value.addOverlay(overlay.value) // 将弹框添加到地图上 } // 关闭弹框 const closeMapPopup = () => { overlay.value.setPosition(undefined) } // 地图点击 const mapClick = (e) => { const lonlat = transform(e.coordinate, 'EPSG:3857', 'EPSG:4326') // 判断当前点击是否点击在图标上 const feature = map.value.forEachFeatureAtPixel(e.pixel, (feature) => feature) if (feature) { // 弹框内容 mapText.value = `<p>${feature.values_.title}<p>` // 把 overlay 显示到指定的坐标位置 overlay.value.setPosition(fromLonLat(feature.values_.long)) } else { // 弹框关闭 closeMapPopup() } } onMounted(() => { init() }) </script> <style scoped> @media (min-width: 1024px) { #map { width: 100%; min-height: calc(100vh); display: flex; align-items: center; } } @media (max-width: 1024px) { #map { width: 100%; height: 500px; margin-top: 20px; } } #popup { background-color: #fff; filter: drop-shadow(0 1px 4px rgba(0, 0, 0, 0.2)); padding: 15px; border-radius: 10px; border: 1px solid #cccccc; top: 0; left: 0; } </style>