前言
最近接手了公司的大屏项目,地图使用了高德地图,将自己的遇到的问题做个简单记录。
- 组件如何渲染到自定义信息窗体内(地图上多种点位,点击之后出现的弹窗内容差异很大,所以每种点位的弹窗都单独使用一个组件,如何信息窗体的content如何拿到组件的dom)
- 点位的label内容更新(点位上方的label内容需要轮询接口更新,如何值更新label内容)
代码
弹窗组件代码
地图上点位弹窗要展示什么, 这个组件就写什么,后面可以完整显示在信息窗体中
<template>
<div class="marker-popup-container">
<span class="close-btn" @click="$emit('close')">x</span>
<div class="popup-content">{{data.name}}-{{data.value}}</div>
</div>
</template>
<script>
export default {
props: {
data: {
type: Object,
default () {
return {}
}
}
}
}
</script>
<style lang="scss" scoped>
.marker-popup-container {
position: relative;
width: 500px;
height: 300px;
border-radius: 5px;
background-color: rgba(0, 0, 0, 0.7);
.close-btn {
position: absolute;
top: 0;
right: 0;
width: 15px;
font-size: 15px;
color: #ccc;
line-height: 1;
cursor: pointer;
}
.popup-content {
text-align: center;
font-size: 20px;
color: #ccc;
}
}
</style>
主要组件代码
<template>
<div class="map-box">
<div class="header">
<el-button type="primary" @click="addCustomIcon">添加自定义图标</el-button>
<el-button type="primary" @click="switchRefreshDataStatus">{{refreshStatua ? '关闭' : '开启'}}刷新数据</el-button>
<el-button type="primary" @click="clearAllMarkers">清空点位</el-button>
</div>
<MapContainer ref="map" @complete="complete=true" />
</div>
</template>
<script>
// import Vue from 'vue'
import Vue from 'vue/dist/vue.esm.js'
import MapContainer from '@/components/MapContainer'
import MarkerPopup from '@/components/MarkerPopup'
export default {
components: {
MapContainer
},
data () {
return {
complete: false, // 地图是否加载完成
refreshStatua: false, // 是否开启了刷新数据
dataList: [
{
id: '1',
name: '杭州地铁1号线',
value: '100',
lng: 120.294282,
lat: 30.172709
},
{
id: '2',
name: '杭州地铁2号线',
value: '200',
lng: 120.304282,
lat: 30.182709
},
{
id: '3',
name: '杭州地铁3号线',
value: '300',
lng: 120.284282,
lat: 30.162709
}
],
markerList: [], // 点位标记列表(需要定时刷新数据的所有点位)
infoWindow: null // 信息窗体
}
},
watch: {
/**
* @introduction 监听器-监听数据变化,刷新点位label内容
* @description 详细描述
*/
dataList: {
handler (val) {
val.forEach(item => {
const { id, value } = item
// 根据id找到当前点位后更新点位的扩展数据
const marker = this.markerList.find(marker => marker.getExtData().id === id)
marker.setExtData({
...marker.getExtData(),
value
})
// 重新设置点位的label内容
this.setLabelContent(marker)
})
},
deep: true
}
},
methods: {
/**
* @introduction 设置信息窗体内容
* @description 详细描述
* @param {参数类型} 参数 参数说明
*/
setInfoWindowContent(infoWindow, item) {
// 创建一个构造器
const Content = Vue.extend({
template: `<MarkerPopup :data="data" @close="closePopup" />`,
components: {
MarkerPopup
},
data () {
return {
data: item
}
},
methods: {
closePopup () {
infoWindow.close()
}
}
})
// 设置信息窗体内容
infoWindow.setContent(new Content().$mount().$el)
},
/**
* @introduction 添加自定义图标
* @description 详细描述
* @param {参数类型} 参数 参数说明
*/
addCustomIcon () {
this.dataList.forEach(item => {
const marker = this.createMarker(item) // 创建点标记
this.markerList.push(marker) // 将点标记添加到点位标记列表中
const group = this.createOverlayGroup() // 创建覆盖物群组
group.addOverlay(marker) // 将点标记添加到覆盖物群组中
this.setLabelContent(marker) // 设置点位标记label内容
this.$refs.map.map.add(group) // 将覆盖物添加到地图上
// 监听覆盖物群组的点击事件
group.on('click', (e) => {
const extData = e.target.getExtData()
if(!this.infoWindow) {
this.infoWindow = new AMap.InfoWindow({
isCustom: true, // 使用自定义窗体
autoMove: true, // 是否自动调整窗体到视野内
content: '', // 窗体内容
offset: new AMap.Pixel(0, -80), // 窗体偏移量
})
}
this.setInfoWindowContent(this.infoWindow, extData)
this.infoWindow.open(this.$refs.map.map, [extData.lng, extData.lat])
})
})
},
/**
* @introduction 切换刷新数据状态
* @description 详细描述
* @param {参数类型} 参数 参数说明
*/
switchRefreshDataStatus () {
this.refreshStatua = !this.refreshStatua
if (!this.refreshStatua) {
return false
}
this.loopRefreshData()
},
/**
* @introduction 设置点位标记label内容
* @description 点位内容是根据点位的扩展数据来设置的,所以需要在创建点位时设置点位的扩展数据
* @param {参数类型} 参数 参数说明
*/
setLabelContent (marker) {
const { name, value } = marker.getExtData()
const content = `<div class="map-label">
<p class="name">${name}</p>
<p class="address">${value}</p>
</div>`
marker.setLabel({
content: content, // 设置文本标注内容
direction: 'center' // 设置文本标注方位
})
},
/**
* @introduction 创建覆盖物群组
* @description 详细描述
* @param {参数类型} 参数 参数说明
*/
createOverlayGroup () {
const group = new AMap.OverlayGroup()
return group
},
/**
* @introduction 创建图标点位
* @description 详细描述
* @param {参数类型} 参数 参数说明
*/
createMarker (data) {
const { lng, lat } = data
const icon = new AMap.Icon({
size: new AMap.Size(220, 82.5), // 图标尺寸
image: require('@/assets/imgs/subwayStations.png'), // Icon的图像
imageSize: new AMap.Size(220, 82.5), // 根据所设置的大小拉伸或压缩图片
})
const marker = new AMap.Marker({
position: new AMap.LngLat(lng, lat),
icon: icon,
offset: new AMap.Pixel(-34, -82.5), // 图标的偏移量,最好设置一下,要不缩放地图时,图标会跑偏
extData: data
})
return marker
},
clearAllMarkers () {
this.markerList.forEach(marker => {
this.$refs.map.map.remove(marker)
})
this.markerList = []
},
/**
* @introduction 刷新数据
* @description 模拟请求
* @param {参数类型} 参数 参数说明
*/
loopRefreshData () {
const timer = setInterval(() => {
if (this.refreshStatua === false) {
clearInterval(timer)
return false
}
console.log('刷新数据')
this.dataList.forEach(item => {
item.value = Math.floor(Math.random() * 1000)
})
}, 3000)
this.$once('hook:beforeDestroy', () => {
clearInterval(timer)
})
}
}
}
</script>
<style lang="scss" scoped>
.map-box {
width: 100%;
height: 100%;
position: relative;
.header {
width: 100%;
height: 60px;
display: flex;
align-items: center;
position: absolute;
top: 0;
left: 0;
background-color: rgba(0, 0, 0, 0.8);
z-index: 1;
}
}
// 覆盖图标label的默认样式
::v-deep .amap-marker-label {
background-color: transparent;
border: none;
}
</style>