隨著大數(shù)據(jù)發(fā)展怯疤,可視化在前端的應(yīng)用越來越廣泛。近期利用高德地圖做了一個(gè)關(guān)于數(shù)據(jù)可視化展示的項(xiàng)目近范,由于數(shù)據(jù)量大嘶摊,使用點(diǎn)標(biāo)記加載慢、重疊多且不美觀评矩,隨后采用聚合標(biāo)記對(duì)問題進(jìn)行解決叶堆。
一、點(diǎn)標(biāo)記
使用AMap.Marker構(gòu)造點(diǎn)標(biāo)記對(duì)象斥杜,最后添加到地圖上虱颗。
/**繪制點(diǎn)標(biāo)記
*@params points {Array} 待渲染的點(diǎn)標(biāo)記數(shù)據(jù)
*/
handleMarker(points) {
for(let i = 0; i < points.length; i++) {
// 如果點(diǎn)標(biāo)記的經(jīng)緯度信息沒有,則不進(jìn)行渲染
if (!points[i].lng) {
continue
}
let marker = new AMap.Marker({
name: points[i].name, // 點(diǎn)標(biāo)記名稱
position: new AMap.LngLat(points[i].lng, points[i].lat), // 點(diǎn)標(biāo)記經(jīng)緯度
// 點(diǎn)標(biāo)記樣式
content: `<div style="background-color: #5366c3; height: 30px; width: 30px; border: 2px solid #ffffff; border-radius: 15px; box-shadow: 0px 0px 12px rgba(0,0,0,0.3);"></div>`,
// 位置偏置量蔗喂,由于默認(rèn)以左上角為基點(diǎn)忘渔,因此x和y方向都偏置一個(gè)半徑的距離,將基點(diǎn)移至圖形中心
offset: new AMap.Pixel(-15, -15),
})
// 添加點(diǎn)標(biāo)注滑入事件
marker.on('mouseover', (e) => {
console.log(e.target.w.name)
.......
})
// 添加點(diǎn)標(biāo)注滑出事件
marker.on('mouseout', (e) => {
.......
})
// 將所有點(diǎn)標(biāo)記存入數(shù)組中缰儿,后續(xù)改造成聚合標(biāo)記時(shí)需要用到
this.markers.push(marker)
}
}
二畦粮、聚合標(biāo)記
當(dāng)需要繪制的點(diǎn)數(shù)量級(jí)非常大時(shí),上述的普通點(diǎn)標(biāo)記渲染的方式就不適用了,在高德地圖可通過AMap.MarkerClusterer這個(gè)插件對(duì)海量點(diǎn)標(biāo)記進(jìn)行聚合顯示宣赔。
/**繪制點(diǎn)標(biāo)記
*/
handleCluster() {
// 聚合標(biāo)注滑入事件
let hanldeBubble = (e) => {
// 滑入聚合標(biāo)記需要執(zhí)行的事件
......
}
// 聚合標(biāo)注滑出事件
let cancleBubble = (e) => {
// 滑出聚合標(biāo)記需要執(zhí)行的事件
......
}
AMap.plugin('AMap.MarkerClusterer', () => {
let count = this.markers.length;
// 設(shè)置聚合標(biāo)記樣式
let _renderClusterMarker = (context) => {
//移除上一次添加的滑入滑出事件预麸,由于每次移動(dòng)、放大縮小地圖都會(huì)重新計(jì)算渲染聚合標(biāo)注儒将,因此必須移除之前的事件吏祸,否則事件會(huì)不斷疊加導(dǎo)致重復(fù)
context.marker.off('mouseover', hanldeBubble)
context.marker.off('mouseout', cancleBubble)
//添加本次產(chǎn)生的新聚合標(biāo)記的滑入滑出事件
context.marker.on('mouseover', hanldeBubble)
context.marker.on('mouseout', cancleBubble)
let extData = []
context.markers.forEach((item) => {
extData.push({
name: item.w.name
})
})
// 將標(biāo)記名稱等存入marker的拓展信息字段extData中
context.marker.extData = extData
let factor = Math.pow(context.count / count, 1 / 18);
let div = document.createElement('div');
let Hue = 180 - factor * 180;
let bgColor = this.bgColor
let fontColor = '#ffffff';
let borderColor = '#ffffff';
let shadowColor = 'rgba(0,0,0,0.3)';
div.style.backgroundColor = bgColor;
//標(biāo)記的大小可以固定寫,也可以根據(jù)聚合點(diǎn)的數(shù)量來決定大小
let size = 30
// let size = Math.round(30 + Math.pow(context.count / count, 1 / 5) * 20);
div.style.width = div.style.height = size + 'px';
div.style.border = 'solid 2px ' + borderColor;
div.style.borderRadius = size / 2 + 'px';
div.style.boxShadow = '0 0 12px ' + shadowColor;
div.innerHTML = context.count;
div.style.lineHeight = (size - 2) + 'px';
div.style.color = fontColor;
div.style.fontSize = '14px';
div.style.textAlign = 'center';
context.marker.setOffset(new AMap.Pixel(-size / 2, -size / 2));
context.marker.setContent(div)
}
this.cluster = new AMap.MarkerClusterer(
this.map, //指定地圖對(duì)象
this.markers, //指定需要聚合的點(diǎn)標(biāo)記對(duì)象
{
// averageCenter: false, // 聚合點(diǎn)的圖標(biāo)位置是否是所有聚合內(nèi)點(diǎn)的中心點(diǎn)
gridSize: 100, //聚合計(jì)算時(shí)網(wǎng)格的像素大小钩蚊,默認(rèn)60
renderClusterMarker: _renderClusterMarker, //聚合點(diǎn)的自定義繪制
zoomOnClick: false, //點(diǎn)擊聚合點(diǎn)時(shí)贡翘,是否散開
})
}
三、滑動(dòng)標(biāo)記彈出氣泡
設(shè)計(jì)氣泡的思路是:以固定定位寫好氣泡樣式两疚,當(dāng)鼠標(biāo)滑入點(diǎn)標(biāo)記時(shí)床估,獲取滑入標(biāo)記的經(jīng)緯度并轉(zhuǎn)換為屏幕x和y方向上的像素位置,賦給氣泡的left和top值诱渤,滑出標(biāo)記時(shí)隱藏氣泡即可。
1谈况、氣泡樣式
html 代碼
<div class="bubble-box" v-show="showBubble" :style="{left: bubleLeft + 'px', bottom: bubleTop + 'px'}">
<div class="bubble-wrap">
<div class="bubble-content" v-for="(item, index) in bubleList" :key="index">
<span></span>
<span>{{item.content}}</span>
</div>
</div>
</div>
css 代碼
.bubble-box {
position: absolute;
margin-left: -6.375rem;
margin-bottom: 1.5625rem;
z-index: 990;
.bubble-wrap {
box-shadow: 0 0 .75rem rgba(0, 62, 255, 0.1);
position: relative;
width: 12.8125rem;
padding: .875rem;
font-size: 1rem;
color: $fontColor;
border-radius: .875rem;
background: #fff;
.bubble-content {
p {
line-height: 1.3;
& span:nth-child(1) {
margin-right: .375rem;
width: .5rem;
height: .5rem;
border-radius: .25rem;
display: inline-block;
background: $fontColor;
}
& span:nth-child(2) {
margin-right: .625rem;
}
}
}
&::after {
content: '';
display: inline-block;
width: .625rem;
height: .625rem;
background: #fff;
position: absolute;
bottom: -0.3125rem;
left: 50%;
margin-left: -0.3125rem;
transform: rotate(45deg);
border-radius: .125rem;
}
}
}
2勺美、滑入事件--顯示氣泡
高德地圖的聚合標(biāo)記是基于點(diǎn)標(biāo)記實(shí)現(xiàn)的,每一次進(jìn)行地圖平移碑韵、縮放操作時(shí)赡茸,AMap.MarkerClusterer插件都會(huì)調(diào)用renderClusterMarker里的方法對(duì)點(diǎn)標(biāo)記根據(jù)gridSize設(shè)置的像素范圍進(jìn)行計(jì)算和渲染,點(diǎn)標(biāo)記若在聚合范圍內(nèi)祝闻,則渲染成一個(gè)聚合點(diǎn)占卧,不在范圍內(nèi)的點(diǎn)標(biāo)記則不做處理(維持原樣式和屬性)。
因此需要對(duì)點(diǎn)標(biāo)記和聚合標(biāo)記分別添加滑入联喘、滑出事件华蜒。
/* 點(diǎn)標(biāo)記滑入事件*/
marker.on('mouseover', (e) => {
this.bubleList = []
//將點(diǎn)標(biāo)記的name字段存入數(shù)組中,點(diǎn)標(biāo)記的自定義數(shù)據(jù)都存放在e.target.w對(duì)象下
this.bubleList.push({
content: e.target.w.name
})
//獲取點(diǎn)標(biāo)記的經(jīng)緯度
let lon = e.target.w.position.R
let lat = e.target.w.position.Q
//通過高德API中的lngLatToContainer將經(jīng)緯度轉(zhuǎn)換為屏幕對(duì)應(yīng)x和y方向的坐標(biāo)
let lnglat = new AMap.LngLat(lon, lat)
let pixel = this.map.lngLatToContainer(lnglat)
//將轉(zhuǎn)換的坐標(biāo)賦給氣泡的left和top值豁遭,顯示氣泡
this.bubleLeft = pixel.x
this.bubleTop = document.body.clientHeight - pixel.y
this.showBubble = true
})
/*聚合標(biāo)記滑入事件*/
let hanldeBubble = (e) => {
this.bubleList = []
//將點(diǎn)標(biāo)記的name字段存入數(shù)組中叭喜,聚合標(biāo)記的自定義數(shù)據(jù)都存放在e.target.extData對(duì)象下
e.target.extData.forEach((item) => {
this.bubleList.push({
content: item.name
})
})
//獲取聚合后標(biāo)記的經(jīng)緯度
let lon = e.target.De.position.R
let lat = e.target.De.position.Q
//通過高德API中的lngLatToContainer將經(jīng)緯度轉(zhuǎn)換為屏幕對(duì)應(yīng)x和y方向的坐標(biāo)
let lnglat = new AMap.LngLat(lon, lat)
let pixel = this.map.lngLatToContainer(lnglat)
this.bubleLeft = pixel.x
this.bubleTop = document.body.clientHeight - pixel.y
this.showBubble = true
}
3、滑出事件--隱藏氣泡
點(diǎn)標(biāo)記和聚合標(biāo)記滑出隱藏氣泡的方法是一樣的蓖谢,不過由于二者綁定事件的方法不同捂蕴,還是需要分開寫。
/*隱藏氣泡*/
hideBubble() {
this.showBubble = false
this.bubleList = []
}
/* 點(diǎn)標(biāo)記滑出事件*/
marker.on('mouseout', (e) => {
this.hideBubble()
})
/*聚合標(biāo)記滑出事件*/
let cancleBubble = (e) => {
this.hideBubble()
}
**4闪幽、聚合標(biāo)記綁定滑入啥辨、滑出事件
高德地圖的點(diǎn)標(biāo)記本身支持滑入、滑出事件盯腌,直接使用marker.on(eventType, function())即可綁定相應(yīng)的事件溉知。而對(duì)于聚合標(biāo)記,官方文檔給出的支持事件只有click點(diǎn)擊事件,因此聚合標(biāo)記的其它事件綁定都需要特殊處理着倾。
上面提到過拾酝,聚合標(biāo)記是基于點(diǎn)標(biāo)記的,每一次對(duì)地圖進(jìn)行縮放等操作時(shí)卡者,都會(huì)重新調(diào)用renderClusterMarker方法計(jì)算和渲染聚合標(biāo)記蒿囤,而實(shí)際上這個(gè)聚合的標(biāo)記是一個(gè)重新計(jì)算了經(jīng)緯度和賦予特殊樣式的點(diǎn)標(biāo)記雷恃,因此可以在renderClusterMarker方法中為聚合標(biāo)記綁定事件臭笆。
AMap.plugin('AMap.MarkerClusterer', () => {
// 設(shè)置聚合標(biāo)記樣式
let _renderClusterMarker = (context) => {
//移除上一次添加的滑入滑出事件,由于每次移動(dòng)汗捡、放大縮小地圖都會(huì)重新計(jì)算渲染聚合標(biāo)注恒傻,因此必須移除之前的事件脸侥,否則事件會(huì)不斷疊加導(dǎo)致重復(fù)
context.marker.off('mouseover', hanldeBubble)
context.marker.off('mouseout', cancleBubble)
//添加本次產(chǎn)生的新聚合標(biāo)記的滑入滑出事件
context.marker.on('mouseover', hanldeBubble)
context.marker.on('mouseout', cancleBubble)
let extData = []
context.markers.forEach((item) => {
extData.push({
name: item.w.name
})
})
// 將標(biāo)記名稱等存入marker的拓展信息字段extData中
context.marker.extData = extData
......
})