「这是我参与11月更文挑战的第7天,活动详情查看:2021最后一次更文挑战」。
⏳ 前言
本人是一只前端🐥,最近在学习到组件的封装时上手封装了高德地图组件供学习使用,这个组件目前有很多需要改正的地方,希望各位大佬给出指正或建议,不胜感激!
🚚 组件需求
-
模板化
我们需要在创建组件实例的时候渲染HTML模板,因此需要将组件模板化
-
可扩展性
地图中的标记点
marker
是根据后端数据动态传入的,也就是说我们需要将数据源传递给Map组件,然后Map组件利用这个数据源将标记点渲染到HTML中 -
api
我们需要根据组件的业务需求,为组件设计api,例如为标记点添加信息窗体、自定义信息窗体内容
🔧 实现
在开始写代码之前,我们先来回顾一下组件的需求:
模板化
- 当用户实例化组件时,需要传入容器的id名
container
和配置选项options
(可选,不传则为默认配置项),注册地图实例并渲染到容器中
设计api
- 用户可以传入标记点数据
markerList
,渲染到地图中 - 为每个标记点
marker
注册信息窗体,并在点击标记点时弹出相应信息窗体 - 用户可以自定义窗体内容,传入信息窗体的标题
title
和内容content
- 为地图加入生命周期和点击事件,监听地图加载完成和点击地图事件
根据上面的需求,我设计了以下6个api
- init() - 初始化地图实例
- renderMarkers(markerList) - 渲染标记点
- registerInfoWindow() - 注册信息窗体
- createInfoWindow(title, content) - 创建信息窗体样式(这部分来自高德地图官方文档)
- onMounted() - 组件生命周期
- handleClickMap() - 地图点击事件
class GaodeMap {
constructor(container, options) {
this.container = container
this.options = options
this.map = {}
this.markers = []
this.init(container, options)
}
init(container, options) {
this.map = new AMap.Map(container, options);
this.map.on('complete', () => {
this.onMounted() // register lifecycle hook for component
})
}
renderMarker(markerList) {
markerList.map(item => {
const marker = new AMap.Marker({
position: new AMap.LngLat(item.Lng, item.Lat),
title: item.title
});
marker.address = item.address // 对应item上的属性address赋值到marker上
this.markers.push(marker)
})
this.map.add(this.markers)
}
registerInfoWindow() {
const content = [];
this.markers.map(marker => {
const title = marker.w.title
const address = marker.address
const infoWindow = new AMap.InfoWindow({
isCustom: true,
content: this.createInfoWindow(title, address, content.join("<br/>")),
offset: new AMap.Pixel(16, -45)
})
AMap.event.addListener(marker, 'click', (e) => {
// 监听标记点点击事件
infoWindow.open(this.map, marker.getPosition())
});
})
}
createInfoWindow(title, content) {
var info = document.createElement("div");
info.className = "custom-info input-card content-window-card";
//可以通过下面的方式修改自定义窗体的宽高
//info.style.width = "400px";
// 定义顶部标题
var top = document.createElement("div");
var titleD = document.createElement("div");
var closeX = document.createElement("img");
top.className = "info-top";
titleD.innerHTML = title;
closeX.src = "https://webapi.amap.com/images/close2.gif";
closeX.onclick = this.closeInfoWindow()
top.appendChild(titleD);
top.appendChild(closeX);
info.appendChild(top);
// 定义中部内容
var middle = document.createElement("div");
middle.className = "info-middle";
middle.style.backgroundColor = 'white';
content += "地址:" + `<a href="javascript:;">${address}</a>`
middle.innerHTML = content;
info.appendChild(middle);
// 定义底部内容
var bottom = document.createElement("div");
bottom.className = "info-bottom";
bottom.style.position = 'relative';
bottom.style.top = '0px';
bottom.style.margin = '0 auto';
var sharp = document.createElement("img");
sharp.src = "https://webapi.amap.com/images/sharp.png";
bottom.appendChild(sharp);
info.appendChild(bottom);
return info;
}
closeInfoWindow() {
return () => {
this.map.clearInfoWindow();
}
}
onMounted() {
// do something when the map is completed
console.log('Map is mounted!');
this.map.on('click', handleClickMarker)
}
handleClickMarker(e) {
console.log('点击了地图的',[e.lnglat.lng, e.lnglat.lat]);
}
}
具体使用:
;(async function() {
const options = {
resizeEnable: true, //是否监控地图容器尺寸变化
zoom: 11, //初始化地图层级
center: [113.122717, 23.008762], //初始化地图中心点
}
const myMap = new GaodeMap('container', options)
const { data: markerList } = await axios.post('http://localhost:3000/api')
myMap.renderMarker(markerList)
myMap.registerInfoWindow()
})();
效果如下图所示:
总结
通过这个高德地图的丐版组件封装,我们可以总结出组件封装的一般步骤:
- 渲染HTML结构
- 设计组件API
- 扩展性
这个丐版的地图组件只是暂时实现了我的业务需求,但它仍有许多不足的地方:
- 需要修改信息窗体内容时需要修改
createInfoWindow
部分的代码,没有做到组件的封装性 - 不能为特定标记点选定特定的图标
最后希望大家提出建议和意见!!!