<template>
<div class="leaflet-map-panel"
ref="leaflet-map-panel"
v-loading="loading">
<div id="map-container"
ref="map">
div>
<poi-search @select="jumpToTargetMarker">poi-search>
<div style="display:none">
<infowindow ref="infowindow"
:deviceInfo="showDeviceInfo">
infowindow>
div>
div>
template>
npm 安装
npm i leaflet --save
or
index.html加入以下代码
..
<body>
...
<link rel="stylesheet" href="https://unpkg.com/[email protected]/dist/leaflet.css" />
<script src="https://unpkg.com/[email protected]/dist/leaflet.js">script>
...
body>
...
个人建议使用npm安装,因为我在使用index.html导入时,在发布版下会报“L未定义”的错误
官方文档
// https://www.cnblogs.com/yingyigongzi/p/10810110.html
// https://www.cnblogs.com/zhonglinke/p/11525965.html
async mapInit() {
try {
// 第一个参数为元素id
this.map.instance = new L.map('map-container', {
// 顺序反的反的!!
center: [this.mapParams.location.lat, this.mapParams.location.lng], // 初始化地图中心点
zoom: 16, // 初始化地图层级
minZoom: 8,
zoomControl: false,
attributionControl: false
})
this.map.markerLayer = new L.featureGroup()
this.map.markerLayer.addTo(this.map.instance)
this.map.otherLayer = new L.featureGroup()
this.map.otherLayer.addTo(this.map.instance)
// 绑定地图click事件
this.map.instance.on('click', this.mapClick)
// 绑定地图移动与缩放事件,就不贴出来了
this.mapRegisterEvent()
// 处理弹出窗自适应事件
this.map.instance.on('autopanstart', () => {
console.log('autopan start')
this.flag.autoPaning = true
this.mapUnregisterEvent()
this.map.instance.once('moveend', () => {
console.log('autopan end')
this.flag.autoPaning = false
this.mapRegisterEvent()
})
})
this.initRecord.mapInit.value = true
} catch (error) {
console.error('mapInit error: ', error)
this.$Message(`${this.$t('gisMap.map.error.init')}: ${error}`, 'error')
this.initRecord.mapInit.value = false
this.initRecord.mapInit.result = error
throw error
}
},
// 在线地图
const tile = new L.tileLayer(
'webrd0{s}.is.autonavi.com/appmaptile?&size=1&scale=1&style=8&x={x}&y={y}&z={z}',
{ subdomains: [ '1', '2', '3', '4' ], format:'image/png'})
tile .addTo(地图实例)
// 离线地图
const wms = new L.tileLayer.wms(
url,// wms地址
options // 参数
)
wms.addTo(地图实例)
async mapLoadTile() {
try {
switch (this.mapParams.useMapType) {
case 'Offline':
if (!this.mapParams.mapTileOptions['layers'])
this.$Message(
`'MapTileOptions.layers' ${this.$t(
'gisMap.map.warning.noCfg'
)}`,
'warning'
)
this.map.tileLayer = new L.tileLayer.wms(
this.mapParams.getUrl(this.lang),
this.mapParams.getOptions()
)
break
case 'Online':
if (!this.mapParams.mapTileOptions['subdomains']) {
this.$Message(`'MapTileOptions.subdomains' ${this.$t( 'gisMap.map.warning.noCfg')}`,'warning')
}
this.mapParams.mapTileOptions.format = this.mapParams.mapTileOptions
.format
? this.mapParams.mapTileOptions.format
: 'image/png'
this.map.tileLayer = new L.tileLayer(
// 直接替换注释即可
// '//webrd0{s}.is.autonavi.com/appmaptile?&size=1&scale=1&style=8&x={x}&y={y}&z={z}'
this.mapParams.getUrl(this.lang),
// { subdomains: [ '1', '2', '3', '4' ], format:'image/png'}
this.mapParams.getOptions()
)
break
default:
throw `Unknow UseMapType '${this.mapParams.useMapType}'\r\nOnly support: 'Offline','Online','Custom'`
}
// 添加到地图
this.map.tileLayer.addTo(this.map.instance)
this.initRecord.tile.value = true
} catch (error) {
this.initRecord.tile.value = false
if (error) {
this.initRecord.tile.result = error
this.$Message(
`${this.$t('gisMap.map.error.loadTile')}: ${error}`,
'error'
)
}
throw error
}
},
// 为地图加载遮罩,只显示默认区域
async mapLoadMask() {
try {
const re = await districtApi.getPolyline(this.mapParams.adCode)
// 内多边形数组
var boundaries = JSON.parse(re)
// 外多边形数组
var outer = [
[90, -360],
[-90, -360],
[-90, 360],
[90, 360]
]
var pathArray = [outer]
pathArray.push.apply(pathArray, boundaries)
// https://leafletjs.com/reference-1.6.0.html#polyline
var polygon = new L.polygon(pathArray, {
// 线条颜色,使用16进制颜色代码赋值。默认值为#006600
color: 'rgb(122,122,122)',
// 线条宽度
weight: 2,
// 轮廓线透明度,取值范围[0,1],0表示完全透明,1表示不透明。默认为0.9
opacity: 0.8,
// 是否用颜色填充路径
fill: true,
// 多边形填充颜色,使用16进制颜色代码赋值,如:#FFAA00
fillColor: 'rgba(250, 250, 250, 0.95)',
// 多边形填充透明度,取值范围[0,1],0表示完全透明,1表示不透明。默认为0.9
fillOpacity: 1
// 轮廓线样式,实线:solid,虚线:dashed
//strokeStyle: 'dashed',
// 勾勒形状轮廓的虚线和间隙的样式,此属性在strokeStyle 为dashed 时有效, 此属性在
// ie9+浏览器有效 取值:
// 实线:[0,0,0]
// 虚线:[10,10] ,[10,10] 表示10个像素的实线和10个像素的空白(如此反复)组成的虚线
// 点画线:[10,2,10], [10,2,10] 表示10个像素的实线和2个像素的空白 + 10个像素的实
// 线和10个像素的空白 (如此反复)组成的虚线
// strokeDasharray: [0, 0, 0]
})
polygon.addTo(this.map.instance)
console.log('蒙版绘制polygon完成')
this.map.maskLayer = polygon
this.initRecord.mask.value = true
} catch (error) {
this.initRecord.mask.value = false
if (error) {
this.initRecord.mask.result = error
this.$Message(
`${this.$t('gisMap.map.error.loadMask')}: ${error}`,
'error'
)
} else {
this.$Message(this.$t('gisMap.map.error.loadMask'), 'error')
}
throw error
} finally {
this.loading = false
}
},
this.map.instance.on('move', (e) => {
if (this.map.maskLayer)
// 会损失性能,自己斟酌把
this.map.maskLayer._renderer._update() //扒对象实例扒到的,_renderer是渲染出来的path
})
mapRegisterEvent() {
this.map.instance.on('moveend', this.mapViewChange)
this.map.instance.on('zoomend', this.mapViewChange)
},
mapUnregisterEvent() {
this.map.instance.off('moveend', this.mapViewChange)
this.map.instance.off('zoomend', this.mapViewChange)
},
async mapViewChange() {
const zoom = this.map.instance.getZoom() // 获取当前地图缩放级别
const center = this.map.instance.getCenter() // 获取当前地图中心位置
const boud = this.map.instance.getBounds()
this.map.otherLayer.clearLayers()
console.log('当前缩放级别:', zoom)
console.log('当前中心位置:', center)
console.log('当前边界:', boud)
if (this.mapParams.searchDistance === 0) {
...
} else if (this.mapParams.searchDistance === -1) {
...
} else if (this.flag.clickMarker && this.flag.autoPaning) {
return
} else if (
this.lastCenter.lat &&
this.lastCenter.lng &&
this.map.instance.distance(center, this.lastCenter) <=
this.mapParams.minMoveDistance
) {
console.log('移动的距离小于' + this.mapParams.minMoveDistance)
return
}
this.fillMarkerDeb()
},
import _markerItem from '../components/markerItem'
const markerItem = Vue.extend(_markerItem)
// ##标记点相关
createMarker(deviceInfo) {
// https://leafletjs.com/reference-1.6.0.html#marker
const marker = new L.marker([deviceInfo.lat, deviceInfo.lng], {
title: deviceInfo.name,
// 鼠标在marker上方时,是否置顶
riseOnHover: true
})
marker.data = deviceInfo
marker.props = {
isTop: false,
loading: false,
animationName: 'uiot-map-marker_add'
}
marker.setAnimation = function (name) {
// if (this.props.animationName) {
// this._icon.classList.remove(this.props.animationName)
// }
this.props.animationName = `uiot-map-marker_${name}`
// this._icon.classList.add(this.props.animationName)
}
marker.setLoading = function (val) {
// console.log(this._icon)
this.props.loading = val
}
marker.setTitle = function (title) {
this._icon.setAttribute('title', title)
}
marker.show = function () {
this._icon.style.display = ''
}
marker.hide = function () {
this._icon.style.display = 'none'
}
marker.setTop = function () {
if (this._map?.$lastTopMarker) {
if (this._map.$lastTopMarker._leaflet_id === this._leaflet_id) {
console.log('setTop 不用置顶', this._map.$lastTopMarker)
return
} else {
console.log('setTop 还原', this._map.$lastTopMarker)
this._map.$lastTopMarker.setZIndexOffset(0)
}
}
this.setZIndexOffset(240)
this._map.$lastTopMarker = this
}
const markerVue = new MarkerConstructor({
propsData: {
marker: marker
}
}).$mount()
marker._vue = markerVue
marker.setIcon(
new L.divIcon({
html: markerVue.$el
})
)
marker.on('click', this.markerClick)
marker.addTo(this.map.markerLayer)
return marker
},
marker.setTitle = function (title) {
//_icon就是图标的dom节点
this._icon.setAttribute('title', title)
}
<template>
<div :class="['marker__wrap',marker.props.animationName]"
v-loading="loading"
ref="panel">
<img :id="marker.data.id"
:class="['marker-icon']"
:src="markerIcon" />
div>
template>
export default {
name: 'MarkerItem',
props: ['marker'],
mounted() {
// setTimeout(() => {
// if (this.marker.props.animationName === 'uiot-map-marker_add')
// this.marker.props.animationName = 'uiot-map-marker_none'
// }, 500)
},
computed: {
loading() {
return this.marker.props.loading
},
markerIcon() {
return getMarkerIcon(this.marker.data)
}
}
}
.leaflet-div-icon {
margin: 0 !important;
width: auto !important;
height: auto !important;
background: transparent !important;
border: none !important;
.marker-icon-panel {
float: left;
display: flex;
// IE需要设置宽度或者最小宽度,不然显示不出来
height: auto;
width: auto;
min-width: 22px;
.marker-icon {
flex: auto;
}
}
}
// 代码见3.1的setTop方法
marker.setTop = function () {
if (this._map?.$lastTopMarker) {
if (this._map.$lastTopMarker._leaflet_id === this._leaflet_id) {
console.log('setTop 不用置顶', this._map.$lastTopMarker)
return
} else {
console.log('setTop 还原', this._map.$lastTopMarker)
this._map.$lastTopMarker.setZIndexOffset(0)
}
}
this.setZIndexOffset(240)
this._map.$lastTopMarker = this
}
.uiot-map-marker_none {
animation: none;
}
.uiot-map-marker_flashing {
animation: flashing 400ms infinite;
animation-fill-mode: forwards;
}
.uiot-map-marker_bounce {
animation: bounce 1000ms infinite;
animation-fill-mode: forwards;
}
let timeout = this.getAnimationTime(animation)
this.dicMarker[id].setAnimation(animation)
console.log('开始动画', this.dicMarker[id])
this.lastJumping.timer = setTimeout(() => {
this.lastJumping.clear()
console.log('取消动画', this.dicMarker[id])
this.dicMarker[id].setAnimation('none')
this.flag.jumpMarker = false
//this.mapRegisterEvent()
this.showMarker(this.dicMarker[id].data)
}, timeout)
marker实例.setLoading(false or true)
createInfoWindow() {
// https://blog.csdn.net/qq_39512863/article/details/90483781
// https://leafletjs.com/reference-1.6.0.html#popup
this.mapInfoWindow = new L.popup({
// 是否自动调整窗体到视野内(当信息窗体超出视野范围时,通过该属性设置是否自动平移地图,使信息窗体完全显示)
autoPaning: true,
// 开启keepInView,在切后台时可能会导致一直触发autupandstart事件,建议关闭
// keepInView: true,
// 弹出位置偏移量,可根据标记点图标大小修改
offset: new L.point(10, 0)
})
console.log('createInfoWindow', this.$refs.infowindow)
this.mapInfoWindow.setContent(this.$refs.infowindow.$el)
// this.mapInfoWindow.on('popupclose', this.infoWindowClosed)
this.map.instance.on('popupclose', this.infoWindowClosed)
},
async showMarker(info, latlng) {
if (info && info.id) {
if (
this.dicMarker[info.id] &&
this.dicMarker[info.id].props.loading === false
) {
this.dicMarker[info.id].setLoading(true)
this.mapInfoWindow.setLatLng(latlng ? latlng : [info.lat, info.lng])
try {
var extend = await markerApi.queryProps(info.id)
this.showDeviceInfo.error = false
this.showDeviceInfo.extend = extend ? extend : {}
} catch (error) {
console.error('showMarker error: ', error)
this.showDeviceInfo.error = true
} finally {
this.showDeviceInfo.baseInfo = info
if (this.dicMarker[info.id]) {
this.dicMarker[info.id].setLoading(false)
}
this.$nextTick(() => {
// 如果直接open, leaflet获取不到infowindow的实际高度的(获取到也是上一次的)
// 所以一定要加上nextTick
this.mapInfoWindow.update()
this.mapInfoWindow.openOn(this.map.instance)
})
}
}
}
},