前言
需求图
需求:
1、绘制当前集团公司在世界分布的资产散点图,
2、双击点击中国地图,进入中国地图
3、右键返回世界地图
4、hover自定义tooltip,并且高亮所在地图区域
实现
- 准备世界地图坐标数据和我国地图坐标数据
地理坐标系组件需要注册地图,我们需要导入并注册地图数据 1、世界地图数据:worldGeoJson.ts 2、中国地图数据:chinaGeoJson.ts 坐标数据过大,我放入我的资源里面了,免费免积分
- 绘制以及option
1、基础结构<template> <div id="ScatterWorldMapContainer" /> </template> ····· <style scoped> #ScatterWorldMapContainer { width: 100%; height: calc(100% - 56px); justify-content: center; position: relative; } </style>
与后端接口协调数据或者自己写mock假数据格式<script setup lang="ts"> import { ref, onUnmounted, defineEmits } from 'vue'; import { ECharts } from 'echarts'; // 调取接口返回的散点数据,通过父组件进行传递数据格式如下图 const props = defineProps({ countryData: { type: Array, default: () => [], }, provinceData: { type: Array, default: () => [], }, cityData: { type: Array, default: () => [], }, });
参数
const scatterChart = ref<ECharts | null>(null);
// echarts对象
const mapStack = ref<any[]>([]);
// 坐标系数组
onUnmounted(() => {
scatterChart.value?.dispose();
});
// 销毁
defineExpose({
initContainer, // 渲染函数
});
// 暴露出去便于父组件接口返回后ref渲染 或者直接initContainer()
2、渲染实现
const initContainer = async () => {
// 导入echarts
const echarts = await import('echarts');
// 导入世界地图
const worldGeoJson = (await import('./worldGeoJson')).default;
// dom绑定
scatterChart.value = echarts.init((document as any).getElementById('ScatterWorldMapContainer'));
// echarts的事件监听
bindOnClickChart(); // 绑定自定义点击事件 用于双击进入中国地图坐标系
bindOnContextmenuChart(); // 绑定自定义右击事件 用于返回世界地图坐标系
bindOncontextHover(); // 绑定自定义hover事件 用于实现选中散点高亮地图所在区域
const mapName = 'world'; // 起始显示世界地图
scole.value = 10; // 控制散点大小的参数
let maxCount = 0; // 数据筛选 用于筛选大于0的数据
const countryDataMap: any = props.countryData.reduce((countryDataMap: any, item: any) => {
countryDataMap.set(item.code, item.count);
if (item.count > maxCount) {
maxCount = item.count;
}
return countryDataMap;
}, new Map());
// 针对世界地图下所有国家数据进行筛选 // countryDataMap {'CHN' => 3000, 'AUS' => 2}
const dataList = (worldGeoJson as any).features.reduce((list: any, item: any) => {
list.push({
name: item.properties.name,
value: countryDataMap.get(item.properties.code) ? countryDataMap.get(item.properties.code) : 0,
});
return list;
}, []);
// 根据countryDataMap绘制世界地图数据
registeRenderMap(mapName, dataList, worldGeoJson, maxCount);
// mapName:world,数据dataList,坐标worldGeoJson,maxCount:0
};
const registeRenderMap = async (mapName: any, dataList: any, geoJson: any, maxCount: number) => {
const echarts = await import('echarts');
// console.log('geoJson', geoJson)
if (geoJson) {
// 注册地图
echarts.registerMap(mapName, geoJson);
}
// 把世界坐标系push到我们的mapStack数组中
pushStack(mapName, dataList, geoJson, maxCount);
// 渲染坐标图
renderMap(mapName, dataList, geoJson, maxCount);
};
// // 把世界坐标系push到我们的mapStack数组中
const pushStack = (mapName: any, dataList: any, geoJson: any, maxCount: number) => {
mapStack.value.push({
mapName,
dataList,
geoJson,
maxCount,
});
};
const renderMap = (mapName: any, dataList: any, geoJson: any, maxCount: number) => {
// console.log('dataList', dataList)
const option = {
geo: { // 地理坐标系组件。地理坐标系组件用于地图的绘制
map: mapName, // 地图类型
roam: true, // 是否开启鼠标缩放和平移漫游。默认不开启
zoom: mapName === 'china' ? 1.2 : 1, // 当前视角的缩放比例。
itemStyle: { // 地图区域的多边形 图形样式。
areaColor: '#DEE3E8', // 地图区域的颜色。
borderColor: '#D9D9D9', // 图形的描边颜色
borderWidth: 0.6, // 描边线宽。为 0 时无描边。
},
scaleLimit: {
min: 1,
max: 4,
},
label: {
normal: { // 静态的时候展示样式
show: false, // 是否显示地图省份得名称
textStyle: {
color: '#fff',
fontSize: 10,
fontFamily: 'Arial',
},
},
emphasis: { // 动态展示的样式
color: '#fff',
},
},
emphasis: {
itemStyle: { // 高亮状态下的多边形和标签样式
color: '#fff',
areaColor: '#6ba7ff',
borderColor: '#fff', // 图形的描边颜色
},
},
},
tooltip: { // 自定义提示信息
padding: 0,
enterable: true,
transitionDuration: 1,
textStyle: {
color: '#000',
decoration: 'none',
},
show: true,
showContent: true,
trigger: 'item',
formatter(params:any) {
let tipHtml = '';
tipHtml = `${'<div style="box-sizing: border-box;box-shadow: 0px 4px 4px rgba(0, 0, 0, 0.08), 0px 0px 12px rgba(0, 0, 0, 0.08);border-radius: 4px;width:140px;background: rgba(0, 0, 0, 0.76);font-size: 9px;padding:1px 8px;">'
+ '<div style="width:86%;height:30px;line-height:30px;">'
+ '<span style="color:#fff;font-size:12px;">'}${params.data.name}</span>`
+ '</div>'
+ '<div style="width: 124px;background: rgba(255, 255, 255, 0.1);margin:6px 0;padding:6px">'
+ '<p style="color:#fff;font-size:12px;">'
+ '资产:'
+ `<span style="color:#FFFFFF;margin:0 6px;">${params.data.val}</span>`
+ '</p>'
'</div>'
return tipHtml;
},
},
series: [
{
name: '风险数量',
type: 'scatter',
coordinateSystem: 'geo',
data: dataList, // series数据内容
symbolSize(val:any) { // 散点大小控制
if (val[2] / 10 !== 0) {
if (scole.value === 20) {
return val[2] / 10 <= 4 ? 4 : val[2] / 10 > 10 && 10 || val[2] / 10
}
return val[2] / 10 <= 8 ? 8 : val[2] / 10 > 20 && 20 || val[2] / 10
}
return 0;
},
symbolKeepAspect: true,
encode: {
value: 2,
},
itemStyle: {
emphasis: {
label: { show: true },
borderColor: '#fff', // 图形的描边颜色
color: {
type: 'radial',
x: 0.5,
y: 0.5,
r: 0.5,
colorStops: [{
offset: 0, color: 'rgba(255, 90, 90, 0.2) ', // 0% 处的颜色
}, {
offset: 0.5, color: 'rgba(255, 90, 90, 1) ', // 100% 处的颜色
}, {
offset: 1, color: 'rgba(255, 90, 90, 1) ', // 100% 处的颜色
}],
global: false, // 缺省为 false
},
},
color: {
type: 'radial',
x: 0.5,
y: 0.5,
r: 0.5,
colorStops: [{
offset: 0, color: '#ff5a5a00', // 0% 处的颜色
}, {
offset: 1, color: '#ff5a5a73', // 100% 处的颜色
}],
global: false, // 缺省为 false
},
},
},
],
};
scatterChart.value?.setOption(option as any, true);
};
-
hover事件
显示提示时,高亮地图相应区域
const bindOncontextHover = () => { // 鼠标hover监听 scatterChart.value?.on('mouseover', (params:any) => { // console.log(params); scatterChart.value?.dispatchAction({ type: 'highlight', // echarts5.0以上支持,5.0以下的需要参考文档,有实现的方式 geoIndex: params.seriesIndex, // 地图相应区域的Index 与下存在一个即可 name: params.name,// 地图相应区域的名称 与上存在一个即可 }); }); scatterChart.value?.on('mouseout', (params:any) => { // console.log(params); // 鼠标离开取消高亮 scatterChart.value?.dispatchAction({ type: 'downplay', // 用 index 或 id 或 name 来指定 geo 组件。 // 可以用数组指定多个 geo 组件。 geoIndex: params.seriesIndex, name: params.name, }); }); }
-
点击事件进入中国地图
const bindOnClickChart = async () => { // 导入中国省份坐标系 const chinaGeoJson = (await import('./chinaGeoJson')).default; // 监听echarts双击事件 scatterChart.value?.on('dblclick', (params: any) => { if (params.name === '中国大陆') { scole.value = 10 // 散点大小控制 // 数据操作 let maxCount = 0; const provinceDataMap: any = props.provinceData.reduce((provinceDataMap: any, item: any) => { provinceDataMap.set(parseInt(item.adcode, 10), item.count); if (item.count > maxCount) { maxCount = item.count; } return provinceDataMap; }, new Map()); const provinceDataList = (chinaGeoJson as any).features.reduce((list: any, item: any) => { list.push({ name: item.properties.name, value: provinceDataMap.get(item.properties.adcode) ? provinceDataMap.get(item.properties.adcode) : 0, }); return list; }, []); // 绘制中国地图 registeRenderMap('china', provinceDataList, chinaGeoJson, maxCount); // 同样在registeRenderMap函数中进行了中国地图push到mapStack坐标数组中 } }); };
-
右击事件返回世界地图
用于从中国地图返回世界地图 上述双击进入中国地图操作后,后我们的坐标系数组mapStack中有两个坐标数据记录 右击事件删除数组的最后一项重新绘制即可返回世界坐标系
// 绑定自定义右击事件 const bindOnContextmenuChart = () => { // 取消右击默认事件 const body = document.getElementById('ScatterWorldMapContainer') as HTMLElement; body.oncontextmenu = (e) => { scole.value = 10 goBack(); e.preventDefault(); }; };
const goBack = () => { // 如果只剩下世界地图 不做操作 if (mapStack.value.length === 1) { return; } mapStack.value.pop(); const { mapName, dataList, geoJson, maxCount, } = mapStack.value.pop(); // 重新绘制世界坐标系 registeRenderMap(mapName, dataList, geoJson, maxCount); };