2023.12.20更新
突然發(fā)現(xiàn)官方更新點聚合調(diào)用方式多包一層mapObj.plugin([“AMap.MarkerClusterer”],fn)來加載聚合功能,之前直接通過new方式不生效,具體可以看下第6點和示例代碼已做更新,感謝讀者反饋。
前言
本文將講述如何利用高德地圖JS API實現(xiàn)地圖標(biāo)點、聚合點、自定義圖標(biāo)、點擊窗體信息展示等基本功能實現(xiàn),結(jié)合實際項目中遇到場景需求進(jìn)行演示和封裝高復(fù)用性組件
一、點聚合是什么?
當(dāng)?shù)貓D中的標(biāo)點很多的時候,縮小地圖層級時候會重疊在一起,這時候可以把局部區(qū)域重疊的點聚合成一個點并標(biāo)注當(dāng)前區(qū)域重合點個數(shù)來展示,隨著地圖縮放層級動態(tài)響應(yīng)式渲染。
二、開發(fā)前準(zhǔn)備
需要到高德開放平臺-控制臺申請key,我的應(yīng)用——添加key——服務(wù)平臺選擇Web端(JS API)
三、API示例
接下來將以Vue項目演示API功能
1.引入高德地圖
入口文件index.html引入高德sdk,key填寫申請的key
<script src="https://webapi.amap.com/maps?v=1.4.15&key=您申請的key值"></script>
2.創(chuàng)建地圖實例
創(chuàng)建一個地圖容器
<div id="map"></div>
`創(chuàng)建一個地圖實例,之后功能將依賴該實例,vue要在mounted周期函數(shù)內(nèi)執(zhí)行
mounted() {
//地圖實例
let map = new AMap.Map(
'map',//地圖容器id
{
resizeEnable: true, //是否監(jiān)控地圖容器尺寸變化
zoom:11, //初始化地圖層級
center: [116.397428, 39.90923], //初始化地圖中心點
// mapStyle:'amap://styles/blue',//地圖樣式(背景)可選,可以在后臺新建自定義樣式
});
}
地圖樣式通過如下設(shè)置:mapStyle:"amap://styles/${theme}"
其中theme值官方集成了多種樣式如下:
如官方提供的樣式不滿足也可以自定義,前往https://geohub.amap.com/mapstyle/index創(chuàng)建
引入創(chuàng)建的ID替換即可 mapStyle:'amap://styles/08539321a17cd7c322f76950f2cxxxxx'
3.添加標(biāo)點
//新建一個標(biāo)點
let marker = new AMap.Marker({
position:[116.397428, 39.90923], //位置
offset: new AMap.Pixel(-13, -30),//偏移
//icon:'', //圖標(biāo)可選,可以使用本地或者在線圖標(biāo)
});
//監(jiān)聽標(biāo)點點擊事件
marker.on('click',e=>{
console.log(e,'click')
})
//標(biāo)點添加到地圖上
map.add(marker)
4.刪除標(biāo)點
刪除一個或者多個標(biāo)點,入?yún)arkers數(shù)組表示標(biāo)點對象集合
let marker = new AMap.Marker({
position:[116.39, 39.90], //位置
});
let marker2 = new AMap.Marker({
position:[117.39, 40.90], //位置
});
map.add(marker)
map.add(marker2)
//刪除第一個標(biāo)點
map.remove([marker]);
5.刪除所有標(biāo)點(覆蓋物)
map.clearMap()
6.聚合點
//添加2個標(biāo)點
let marker = new AMap.Marker({
position:[116.397428, 39.90923], //位置
});
let marker2 = new AMap.Marker({
p position:[116.3680, 39.9200], //位置
});
map.add(marker)
map.add(marker2)
/*設(shè)置聚合
*@param map:地圖實例
*@param markers:標(biāo)點對象數(shù)組
*/
map.plugin(["AMap.MarkerClusterer"],()=> {
let cluster= new AMap.MarkerClusterer(map, markers, {
gridSize: 80,
});
});
未聚合
聚合效果:
7.自定義聚合點樣式
聚合點自定義樣式通過設(shè)置renderClusterMarker字段配置渲染函數(shù),并在渲染函數(shù)中通過dom操作生成樣式節(jié)點插入聚合點父節(jié)點上
//聚合點實例
let cluster = new AMap.MarkerClusterer(map, markers, {
gridSize: 80,
renderClusterMarker:renderClusterMarker,//自定義樣式渲染
});
//渲染函數(shù)
function renderClusterMarker(context) {
var div = document.createElement("div");
div.style.width = "50px";
div.style.height = "50px";
div.style.lineHeight = "50px";
div.style.backgroundImage = `url(/static/images/icon.png)`;//自定義圖標(biāo)背景
div.style.backgroundSize = "100%";
div.style.backgroundRepeat = "no-repeat";
div.innerHTML = context.count;//聚合個數(shù)
div.style.color = "#fff";
div.style.fontSize = "16px";
div.style.paddingBottom = "10px";
div.style.boxSizing = "border-box";
div.style.textAlign = "center";
var size = Math.round(
30 + Math.pow(context.count / markers.length, 1 / 5) * 20//markers所有標(biāo)點對象集合
);
context.marker.setOffset(new AMap.Pixel(-size / 2, -size / 2));
context.marker.setContent(div);
}
8.清除聚合
每次重新渲染設(shè)置聚合需要清除之前,不然數(shù)量會疊加
//cluster:聚合點實例
cluster&&cluster.setMap(null);
9.打開窗體信息
//新建一個標(biāo)點
let marker = new AMap.Marker({
position:[116.397428, 39.90923], //位置
offset: new AMap.Pixel(-13, -30),//偏移
//icon:'', //圖標(biāo)可選,可以使用本地或者在線圖標(biāo)
});
//監(jiān)聽標(biāo)點點擊事件(顯示窗體信息)
marker.on('click',e=>{
//創(chuàng)建窗體實例
let infoWindow =new AMap.InfoWindow({
content:'test',//窗體內(nèi)容,支持插入dom.innerHTML
anchor:'top-right'//錨點,窗體相對鼠標(biāo)點擊位置
});
//顯示窗體
//map:地圖實例,[lng,lat]:窗體在地圖中位置
infoWindow.open(map,[e.lnglat.lng,e.lnglat.lat])
})
//標(biāo)點添加到地圖上
map.add(marker)
anchor可取值:top-left、top-center、top-right、middle-left、center、middle-right、bottom-left、 bottom-center、bottom-right
四、實戰(zhàn)開發(fā)
需求要求
1.假設(shè)需要在地圖上標(biāo)注各種工程項目位置,工程項目分為3中類型,在建工程,已完成工程,延期工程不同類型分別對應(yīng)不同圖標(biāo)
2.實現(xiàn)聚合功能,聚合圖標(biāo)自定義
3.點擊對應(yīng)工程項目彈窗顯示項目信息
4.外部有搜索條件可以進(jìn)行數(shù)據(jù)搜索,搜索完重新渲染地圖,比如搜索xxxx至xxxx時間內(nèi)在建工程,或者根據(jù)類型搜索等
5.大屏功能進(jìn)行地圖樣式換膚
6.封裝成通用組件方便下次開發(fā)使用
效果圖如下:
封裝思路分析
怎樣封裝才能方便使用呢?
對于組件封裝我們可以采用倒推法,先寫父組件里面的引用然后倒推實現(xiàn)子組件邏輯。
我們很容易想到在父組件內(nèi)這樣引用地圖組件
<amap :center="mapCenter" :zoom="zoom" :markers="markers"></amap>
傳入地圖中心點(center)、層級(zoom)以及標(biāo)點經(jīng)緯度數(shù)組(markers)就能自動渲染,有了這個錐形后我們在繼續(xù)擴(kuò)展。
聚合樣式和信息窗體要如何設(shè)計才能適應(yīng)不同場景的自定義呢?
對于vue自定義內(nèi)容首當(dāng)其沖能想到的當(dāng)然是slot,用插槽形式暴露給調(diào)用方就能自由diy
<amap :center="mapCenter" :zoom="zoom" :markers="markers">
<!-- 聚合樣式 -->
<template v-slot:cluster>
</template>
<!-- 窗體樣式 -->
<template v-slot:infoWindow>
</template>
</amap>
整個調(diào)用我們已經(jīng)推導(dǎo)出來了,但還有一個問題,窗體或者聚合插槽中渲染數(shù)據(jù)要怎么樣拿到?這個數(shù)據(jù)是和每個標(biāo)點一一對應(yīng)。我們可以通過標(biāo)點參數(shù)(markers)傳入數(shù)據(jù)在通過作用域插槽傳出,最終成型為:
<amap :center="mapCenter" :zoom="zoom" :markers="markers">
<!-- 聚合樣式 -->
<template v-slot:cluster>
</template>
<!-- 窗體樣式 -->
<template v-slot:infoWindow="{ data }">
</template>
</amap>
很可惜經(jīng)過研究聚合點個數(shù)數(shù)據(jù)無法通過作用域插槽傳出,我們可以在封裝的組件中通過dom操作直接在插槽節(jié)點內(nèi)添加一個span節(jié)點寫入個數(shù)居中顯示,寫死這個渲染節(jié)點,這樣除了這個數(shù)字剩下圖標(biāo)樣式都可以通過插槽自定義。
完整代碼
先封裝高德地圖工具類
amap.js
/**
* 高德地圖工具類
*/
class amap {
/**
* 構(gòu)造函數(shù)
* @param id :地圖容器id
* @param params 地圖配置參數(shù)
*/
constructor(id, params) {
this.markers = [];//所有標(biāo)點數(shù)組集合
this.cluster=null;//聚合點實例
this.map = new AMap.Map(id, {
...params
});
}
/**
* 添加標(biāo)點
* @param markers:標(biāo)點數(shù)組,item支持經(jīng)緯度或者對象
* @param clickEvent:標(biāo)點點擊事件回調(diào)
*/
addMarkers(markers = [], clickEvent = () => { }) {
for (let item of markers) {
let params = {
offset: new AMap.Pixel(-13, -30)
};
if (Array.isArray(item)) {
params.position = item;
} else if (typeof item === "object") {
params = { ...item,...params };
}
//新建一個標(biāo)點
let marker = new AMap.Marker(params);
//標(biāo)點點擊事件
marker.on("click", (e) => {
typeof clickEvent === 'function' && clickEvent({ ...params, lnglat: e.lnglat })
});
//標(biāo)點添加到地圖上
this.map.add(marker);
//保存到實例
this.markers.push(marker)
}
}
//清空地圖覆蓋物
clearMap() {
this.markers=[]
this.map.clearMap();
}
/**
* 聚合點
* @param renderClusterMarker:聚合點自定義渲染函數(shù)
*/
clusterMarker(renderClusterMarker) {
//清除之前的聚合
this.cluster&&this.cluster.setMap(null);
//設(shè)置聚合
this.map.plugin(["AMap.MarkerClusterer"],()=> {
this.cluster= new AMap.MarkerClusterer(this.map, this.markers, {
gridSize: 80,
renderClusterMarker: renderClusterMarker
});
});
}
//打開信息窗口
showInfoWindow({ lng, lat, ...params }) {
//創(chuàng)建窗體實例
let infoWindow = new AMap.InfoWindow(params);
//顯示窗體
//map:地圖實例,[lng,lat]:窗體在地圖中位置
infoWindow.open(this.map, [lng, lat])
}
//關(guān)閉信息窗口
closeInfoWindow() {
this.map.clearInfoWindow();
}
}
export default amap
高德地圖組件
amap.vue
<template>
<div id="amap-container" class="amap-container" :style="layoutStyle">
<!-- 自定義渲染樣式 -->
<div class="cust-cluster-wrap">
<slot name="cluster"></slot>
</div>
<div class="cust-infoWindow-wrap">
<slot name="infoWindow" :data="currentMarkerData"></slot>
</div>
</div>
</template>
<script>
import amap from "./amap";
export default {
name: "Amap",
props: {
//地圖寬單位px
width: {
type: [Number, String],
default: "100%",
},
//地圖高單位px
height: {
type: [Number, String],
default: "100%",
},
//地圖實例化參數(shù)
mapParams: {
type: Object,
default: () => {},
},
//地圖中心點
center: {
type: Array,
default: () => [116.397428, 39.90923],
},
//地圖層級
zoom: {
type: Number,
default: 11,
},
//標(biāo)點
markers: {
type: Array,
default: () => [],
},
//是否聚合點
isCluster: {
type: Boolean,
default: true,
},
//點擊標(biāo)點是否顯示信息窗口
isShowInfoWindow: {
type: Boolean,
default: true,
},
//信息窗口配置參數(shù)
infoWindowParams: {
type: Object,
default: () => {},
},
//是否點擊地圖關(guān)閉信息窗口
closeIwOnClickMap: {
type: Boolean,
default: true,
},
},
data() {
return {
map: null, //地圖實例
cluster: null, //聚合點實例
currentMarkerData: {},
};
},
computed: {
//設(shè)置地圖容器寬高
layoutStyle() {
//%或者px兼容處理
const getAttrVal = (val) =>
val.toString().includes("%") ? val : `${val}px`;
return {
width: getAttrVal(this.width),
height: getAttrVal(this.height),
};
},
//是否自定義聚合點樣式
isCustcluster() {
return this.$scopedSlots.cluster;
},
//是否自定義信息窗口
isCustInfoWindow() {
return this.$scopedSlots.infoWindow;
},
},
watch: {
//監(jiān)聽標(biāo)點數(shù)據(jù)重新渲染
markers: {
handler(val) {
if (this.map) {
//清空地圖標(biāo)點
this.map.clearMap();
//重新渲染
this.addMarkers(val);
this.isCluster && this.clusterMarker(); //設(shè)置聚合點
}
},
immediate: false,
deep: true,
},
},
mounted() {
this.createMap(); //創(chuàng)建地圖
this.addMarkers(this.markers); //添加標(biāo)點
this.isCluster && this.clusterMarker(); //設(shè)置聚合點
},
beforeDestroy() {
//銷毀地圖
this.map && this.map.map.destroy();
},
methods: {
//創(chuàng)建地圖實例
createMap() {
this.map = new amap("amap-container", {
...this.mapParams,
zoom: this.zoom,
center: this.center,
});
//地圖加載完成
this.map.map.on("complete", () => {
this.$emit("initComplete");
});
//地圖點擊事件
this.map.map.on("click", (e) => {
this.closeIwOnClickMap&&this.closeInfoWindow()
this.$emit("mapClick", e);
});
},
//標(biāo)點
addMarkers(markers = []) {
this.map.addMarkers(markers, (e) => {
this.currentMarkerData = e;
//點擊標(biāo)點顯示信息窗口
if (this.isShowInfoWindow) {
//等待currentMarkerData數(shù)據(jù)渲染更新完成在打開信息窗口
this.$nextTick(() => {
this.map.showInfoWindow({
lat: e.lnglat.lat,
lng: e.lnglat.lng,
...this.infoWindowParams,
isCustom: this.isCustInfoWindow,
content: this.getCustInfoWindowDom() || e.infoWindowContent || "",
});
});
}
//派發(fā)標(biāo)點點擊事件
this.$emit("markerClick", e);
});
},
//聚合標(biāo)點
clusterMarker() {
//自定義渲染函數(shù)
function renderClusterMarker(context) {
//獲取自定義聚合點DOM
let custClusterDom =
document.getElementsByClassName("cust-cluster-wrap")[0];
let div = document.createElement("div");
div.innerHTML = custClusterDom.innerHTML;
let span = document.createElement("span");
span.style.position = "absolute";
span.style.top = "50%";
span.style.left = "50%";
span.style.transform = "translate(-50%,-50%)";
span.style.zIndex = "99";
//設(shè)置聚合數(shù)
span.innerHTML = context.count;
//插入聚合數(shù)量span節(jié)點
div.children[0].appendChild(span);
let size = Math.round(
30 + Math.pow(context.count / this.map.markers.length, 1 / 5) * 20
);
context.marker.setOffset(new AMap.Pixel(-size / 2, -size / 2));
context.marker.setContent(div);
}
//聚合
this.map &&
this.map.clusterMarker(
this.isCustcluster ? renderClusterMarker.bind(this) : undefined
);
},
//獲取自定義窗口Dom
getCustInfoWindowDom() {
if (!this.isCustInfoWindow) return;
return document.getElementsByClassName("cust-infoWindow-wrap")[0]
.innerHTML;
},
//關(guān)閉信息窗口
closeInfoWindow() {
this.map.closeInfoWindow();
},
},
};
</script>
<style lang="scss" scoped>
.amap-container {
width: 100%;
height: 100%;
}
.cust-cluster-wrap {
position: fixed;
top: 0;
left: 0;
transform: translate(-100%, -100%);
}
.cust-infoWindow-wrap {
position: fixed;
top: 0;
left: 0;
transform: translate(-100%, -100%);
}
</style>
頁面調(diào)用:
inde.vue
<template>
<div class="container">
<!-- 地圖區(qū)域 -->
<div class="map-wrap">
<amap
:center="mapCenter"
:zoom="zoom"
:mapParams="mapParams"
:markers="markers"
:infoWindowParams="infoWindowParams"
isCluster
@initComplete="onInitComplete"
@mapClick="onMapClick"
>
<!-- 聚合樣式 -->
<template v-slot:cluster>
<div class="cluster">
<img class="icon" src="/static/images/icon.png" />
</div>
</template>
<!-- 窗體樣式 -->
<template v-slot:infoWindow="{ data }">
<div class="infoWindow">
<div class="name">{{ data.projectName }}</div>
<div class="row">電話:{{ data.phone }}</div>
<div class="row">地址:{{ data.address }}</div>
</div>
</template>
</amap>
</div>
<!-- 搜索按鈕 -->
<button class="search" @click="onSearch">搜索(模擬刷新數(shù)據(jù))</button>
</div>
</template>
<script>
import amap from "./component/amap/amap.vue";
export default {
components: {
amap,
},
data() {
return {
markers: [], //標(biāo)點集合
mapCenter: [116.397428, 39.90923],
zoom: 13,
mapParams: {
mapStyle: "amap://styles/blue", //地圖樣式
},
infoWindowParams: {
anchor: "top-right",
offset: new AMap.Pixel(0, 15), //偏移
},
};
},
created() {
this.onSearch();
},
mounted() {},
methods: {
//加載完成
onInitComplete() {
console.log("加載完成");
},
//點擊地圖
onMapClick(e) {
console.log(e, "點擊地圖");
},
//搜索
onSearch() {
this.markers = [];
this.$nextTick(() => {
//模擬接口生成數(shù)據(jù)
for (let i = 0; i < parseInt(Math.random() * 20); i++) {
let [lng, lat] = this.mapCenter;
let position = [lng + i * Math.random() * 0.05, lat + i * 0.01];
this.markers.push({
position,//經(jīng)緯度
icon: `/static/images/map_icon${
parseInt(Math.random() * 3) + 1
}.png`,//標(biāo)點圖標(biāo)
projectName: `項目${i}`,//項目名稱
phone: "13333333333",//電話
address: "北京市朝陽區(qū)望京阜榮街10號",//地址
});
}
});
},
},
};
</script>
<style lang="scss" scoped>
.container {
width: 100%;
height: 100%;
}
.map-wrap {
height: 90%;
width: 100%;
.cluster {
height: 60px;
width: 60px;
border-radius: 50%;
color: #fff;
font-size: 16px;
line-height: 50px;
.icon {
width: 100%;
position: absolute;
top: 0;
left: 0;
height: 100%;
}
}
.infoWindow {
padding: 20px;
box-sizing: border-box;
border-radius: 10px;
background: #fff;
.name {
font-size: 18px;
color: rgb(39, 130, 248);
}
.row {
margin-top: 10px;
font-size: 14px;
}
}
}
.search {
margin-top: 10px;
width: 150px;
height: 40px;
}
</style>
效果
文章來源:http://www.zghlxwxcb.cn/news/detail-757310.html
搜索刷新后
文章來源地址http://www.zghlxwxcb.cn/news/detail-757310.html
到了這里,關(guān)于web JS高德地圖標(biāo)點、點聚合、自定義圖標(biāo)、自定義窗體信息、換膚等功能實現(xiàn)和高復(fù)用性組件封裝教程的文章就介紹完了。如果您還想了解更多內(nèi)容,請在右上角搜索TOY模板網(wǎng)以前的文章或繼續(xù)瀏覽下面的相關(guān)文章,希望大家以后多多支持TOY模板網(wǎng)!