什么是ECharts
ECharts是一款基于JavaScript的数据可视化图表库,提供了丰富的图表,包括了常规的折线图、柱状图、散点图、饼图、K线图,用于统计的盒形图,用于地理数据可视化的地图、热力图、线图,用于关系数据可视化的关系图、treemap、旭日图,多维数据可视化的平行坐标,还有用于 BI 的漏斗图,仪表盘,并且支持图与图之间的混搭。是一款成熟、易用、广泛使用的JS图标库。
官网地址:echarts.apache.org/zh/index.ht…
伪3D地图的需求
之前我也做过一些大屏开发,用的基本上都是ECharts,少部分用的是G2,所以基本上是对ECharts很熟悉了,只不过之前都是用ECharts做图表,这次需要在大屏中间放一块中国地图,并且支持下转到各个省,每个区域支持选中和做toolTip提示。
- 展示中国地图
- 可以下转到各个省
- 支持选中和展示toolTips
- 最好有3D效果
- 有随机选中区域的动态效果,hover的时候不展示随机效果
- 其他图表(不在这篇文章的讨论范围)
实现思路
- 通过多张地图叠加产生3D的效果
- 通过点击事件,获取下级地图数据,重新配置ECharts的Option刷新地图。
数据来源(阿里云数据可视化平台):datav.aliyun.com/portal/scho…
具体的实现
初始化ECharts
1.安装ECharts
npm install ECharts
2.引入ECharts
import * as echarts from "echarts";
3.初始化ECharts
- 通过id初始化了ECharts
- 加载了中国地图
- 初始化点击事件,实现下转到省
export class BigScreenMap {
myChart = null
constructor({ id }) {
this.myChart = echarts.init(document.getElementById(id)); // 初始echarts
//加载中国地图
this.toMap({
baseMapName: "china",
mainMapName: "empchina"
});
this.myChart.on("click", params => {
// 处理点击事件
try {
if (params.name) {
// 点击下转到省
this.toMap({
baseMapName: params.name,
mainMapName: params.name
});
}
} catch (error) {}
});
// 动态选中效果
this.dynamicSelectOrg();
}
}
4.加载地图
这里通过判断地图是否已经注册了,如果地图未注册则请求具体的地图参数,并且注册到ECharts里面
async toMap(options) {
try {
if (proviceNameObject[options.baseMapName]) {
if (!this.mapCache[options.baseMapName]) {
const res = await getMapJson(proviceNameObject[options.baseMapName]);
echarts.registerMap(options.baseMapName, res);
}
if (!this.mapCache[options.mainMapName]) {
const res = await getMapJson(proviceNameObject[options.mainMapName]);
echarts.registerMap(options.mainMapName, res);
}
this.baseMapName = options.baseMapName;
this.mainMapName = options.mainMapName;
const option = this.createOption();
this.myChart.clear();
this.myChart.setOption(option);
this.mapCache(options);
}
} catch (error) {}
}
5.设置ECharts参数
- 使用多张地图叠加错位造成视觉上的3D效果
- 加载了撒点
//设置参数
createOption() {
// 省会城市Coordinate 隐射表
const provinceCoordinates = {
安徽: [117.194778, 31.86577],
北京: [116.403694, 39.949459],
重庆: [106.553263, 29.556681],
福建: [119.292069, 26.144144],
甘肃: [103.856737, 36.094212],
广东: [113.239359, 23.185545],
广西: [108.345678, 22.861984],
贵州: [106.616332, 26.707352],
海南: [110.350983, 19.968035],
河北: [114.508772, 38.083783],
河南: [113.644099, 34.769161],
黑龙江: [126.522207, 45.801617],
湖北: [114.361594, 30.601078],
湖南: [112.926605, 28.217167],
吉林: [125.326383, 43.797768],
江苏: [118.832137, 32.038322],
江西: [115.851775, 28.672488],
辽宁: [123.486653, 41.682522],
内蒙古: [111.785972, 40.849642],
宁夏: [106.257585, 38.482579],
青海: [101.851432, 36.622494],
山东: [117.194778, 36.652148],
山西: [112.595453, 37.858034],
陕西: [109.026378, 34.350591],
上海: [121.518142, 31.211845],
四川: [104.132697, 30.561282],
天津: [117.286764, 39.001295],
西藏自治区: [91.144205, 29.649484],
新疆维吾尔自治区: [87.667116, 43.817754],
云南: [102.881681, 24.866897],
浙江: [120.211934, 30.274265],
香港: [114.242011, 22.272474],
澳门: [113.579709, 22.169692],
台湾: [121.591732, 25.034634]
};
// 需要展示的省会城市数据
const getProvincialCapitals = names => {
if (this.provicePoint && this.provicePoint.length > 0) return this.provicePoint;
let res = [];
const len = names.length;
for (let i = 0; i < len; i++) {
let geoCoord = provinceCoordinates[names[i]];
if (geoCoord) {
res.push({
name: names[i],
value: geoCoord
});
}
}
return res;
};
const positionY = this.baseMapName === "china" ? 70 : 50;
const layoutSize = this.baseMapName === "china" ? 120 : 100;
let option = {
tooltip: {
show: true
},
geo: [
// 边框 map
{
zlevel: 6,
id: "mainMapName",
map: this.mainMapName,
// 取消一些交互
// 位置大小调整
layoutCenter: ["50%", `${positionY}%`], //位置
layoutSize: `${layoutSize}%`, //大小
// 框颜色
itemStyle: {
color: "transparent",
opacity: 1,
borderWidth: 1,
borderColor: "#73A8FF",
borderCap: "round",
shadowBlur: 20,
shadowColor: "#97BBE4"
},
tooltip: {
trigger: "item",
show: true,
enterable: true,
formatter: this.toolTipHtmlFun,
// position: [10, 10],
backgroundColor: "transparent",
borderColor: "transparent",
extraCssText: "box-shadow: none;",
borderWidth: 0,
padding: 0
},
emphasis: {
itemStyle: {
areaColor: "#6BA2FE",
color: "#6BA2FE"
}
}
},
// 顶层map
{
map: this.baseMapName,
zlevel: 5,
z: 4,
// 位置大小调整
layoutCenter: ["50%", `${positionY}%`], //位置
layoutSize: `${layoutSize}%`, //大小
// 调整颜色
itemStyle: {
// areaColor:'#264684',
areaColor: "#032A74",
borderWidth: 1.2,
borderCap: "round",
borderColor: "RGBA(81, 123, 165, 1)"
// shadowColor: "#fff",
// shadowBlur: 20,
}
},
// 第二层
{
// 下层地图,防3d效果
zlevel: 5,
z: 3,
map: this.mainMapName,
silent: true,
// 位置大小调整
layoutCenter: ["50.07%", `${positionY + 0.1}%`], //位置
layoutSize: `${layoutSize}%`, //大小
// 颜色
itemStyle: {
borderWidth: 0,
borderCap: "round",
areaColor: "#B8FFFF"
}
},
// 第三层
{
// 下层地图,防3d效果
zlevel: 5,
z: 2,
map: this.mainMapName,
silent: true,
// 位置大小调整
layoutCenter: ["50.14%", `${positionY + 1}%`], //位置
layoutSize: `${layoutSize}%`, //大小
// 颜色
itemStyle: {
borderWidth: 0,
borderCap: "round",
areaColor: "#B8FFFF"
}
},
// 第四层
{
// 下层地图,防3d效果
zlevel: 5,
z: 1,
map: this.mainMapName,
silent: true,
// 位置大小调整
layoutCenter: ["50.21%", `${positionY + 1.3}%`], //位置
layoutSize: `${layoutSize - 0.5}%`, //大小
// 颜色
itemStyle: {
borderColor: "rgba(0, 0, 0, 0.3)",
borderWidth: 1,
borderCap: "round",
areaColor: "#B8FFFF",
shadowBlur: 10,
shadowColor: "rgba(0, 0, 0, 0.6)"
}
}
],
series: [
// 其他省份
{
name: "otherProvincialCapital",
// symbol 形状修改
type: "scatter",
symbol: "circle",
symbolSize: this.baseMapName != "china" ? [34, 49] : 4,
// //背景
symbol: this.baseMapName != "china" ? "image://" + mapPointBG : "",
symbolOffset: [0, "-100%"],
// 显示层级
zlevel: 7,
z: 1,
coordinateSystem: "geo",
geoIndex: 1,
itemStyle: {
//坐标点颜色
show: true,
color: "#fff"
},
// label样式
label: {
show: this.baseMapName != "china" ? false : true,
position: "top",
formatter: "{b}",
fontSize: 10,
color: "rgba(255,255,255,0.8)",
emphasis: {
show: true
},
padding: 3,
borderWidth: 0,
borderRadius: 2,
// borderColor: "rgba(7, 19, 37, 1)",
fontWeight: 300,
backgroundColor: "rgba(255, 255, 255, 0.1)" // 字体背景色
},
silent: true,
data: getProvincialCapitals(this.needShowProvincialCapitals)
}
]
};
return Object.freeze(option);
}
6.动态选中区域
- 通过定时器随机高亮和showTip展示选中效果
//是否要动态
dynamicSelectOrg() {
let timer = null;
let timeoutTimer = null;
this.myChart.on("mouseover", params => {
// mouseover
clearInterval(timer);
clearTimeout(timeoutTimer);
this.unselectedMapOrigin();
this.selectName = "";
});
this.myChart.on("globalout", params => {
//globalout
clearInterval(timer);
clearTimeout(timeoutTimer);
timeoutTimer = setTimeout(() => {
timer = setInterval(() => {
this.selectedMapOrign();
}, 3000);
}, 3 * 1000);
});
timer = setInterval(() => {
this.selectedMapOrign();
}, 3000);
}
selectedMapOrign() {
try {
this.unselectedMapOrigin();
const randomNum = this.getRandomInt(0, this.mapNameData[this.mainMapName].length);
this.selectName = this.mapNameData[this.mainMapName][randomNum];
//高亮
this.myChart.dispatchAction({
type: "highlight",
geoId: "mainMapName",
name: this.selectName
});
//showTip
this.myChart.dispatchAction({
type: "showTip",
geoId: "mainMapName",
name: this.selectName
});
} catch (error) {}
}
unselectedMapOrigin() {
try {
if (this.selectName) {
//高亮
this.myChart.dispatchAction({
type: "downplay",
geoId: "mainMapName",
name: this.selectName
});
}
} catch (error) {}
}