近期做了一些echarts图表的开发,都是基本的单轴和双轴的折线图、饼图、柱状图等,看到百度疫情实时地图,对中国地图和各个省份地图充满了好奇,想着通过echarts模仿实现百度疫情实时地图。
整体代码在vue中实现,跟普通的html和js中实现并无太大差距,差距主要体现在vue的数据双向绑定上。
1.创建vue项目,配置路由相关等设置,创建展示的页面。
<template>
<div class="home">
</div>
</template>
<script>
export default {
name: 'ChinaMap'
}
</script>
<style scoped>
</style>
2.vue中获取并引入 ECharts、引入Echarts中的中国地图
(1)在控制台中执行命令:npm install echarts --save,安装完成后vue项目中就获取到了echarts,在依赖文件夹node_modules中,即可找到获取到的echarts:
(2)项目中引入echart以及echarts提供的中国地图
<template>
<div class="home">
</div>
</template>
<script>
import echarts from 'echarts'
import china from 'echarts/map/js/china'
export default {
name: 'ChinaMap'
}
</script>
<style scoped>
</style>
3.初始化地图
(1)代码中dataList数据为模拟的各个省市的疫情数据,
(2)id为"main"的div块为存放地图的容器(echarts的相关学习可以去官网查看)。
<template>
<div class="home">
<!-- 放地图的容器-->
<div id='main' ref='chart' style='width: 100%;height:600px;'></div>
</div>
</template>
<script>
import echarts from 'echarts'
import china from 'echarts/map/js/china'
export default {
name: 'ChinaMap',
data() {
return {
// 地图上显示的疫情数据
dataList : [
{ name: '南海诸岛', value: 0 },
{ name: '北京', value: 54 },
{ name: '天津', value: 13 },
{ name: '上海', value: 40 },
{ name: '重庆', value: 75 },
{ name: '河北', value: 13 },
{ name: '河南', value: 83 },
{ name: '云南', value: 11 },
{ name: '辽宁', value: 19 },
{ name: '黑龙江', value: 15 },
{ name: '湖南', value: 69 },
{ name: '安徽', value: 60 },
{ name: '山东', value: 39 },
{ name: '新疆', value: 2 },
{ name: '江苏', value: 31 },
{ name: '浙江', value: 104 },
{ name: '江西', value: 36 },
{ name: '湖北', value: 1052 },
{ name: '广西', value: 33 },
{ name: '甘肃', value: 7 },
{ name: '山西', value: 9 },
{ name: '内蒙古', value: 7 },
{ name: '陕西', value: 22 },
{ name: '吉林', value: 4 },
{ name: '福建', value: 18 },
{ name: '贵州', value: 5 },
{ name: '广东', value: 98 },
{ name: '青海', value: 1 },
{ name: '西藏', value: 0 },
{ name: '四川', value: 44 },
{ name: '宁夏', value: 4 },
{ name: '海南', value: 22 },
{ name: '台湾', value: 3 },
{ name: '香港', value: 5 },
{ name: '澳门', value: 5 }
]
}
},
methods: {
initChart() {
// 获取存放地图的dom元素
let myChart = echarts.init(this.$refs.chart);
// 指定图表的配置项和数据
let option = {
// 地图标题
title: {
text: '中国疫情地图',
x: 'center'
},
// 放上鼠标后显示的新
tooltip : {
trigger: 'item',
formatter: function(e, t, n) {
return '地区:' + e.data.name + '<br/>确诊:' + e.data.value
},
textStyle:{
align:'left'
}
},
// 设置地图数据
series: [
{
type: 'map',
map: 'china',
label: {
show: true,
color: 'black',
fontsize: 12,
formatter: res=> {
return res.data.name
}
},
itemStyle: {
// borderColor: 'red',
borderWidth: .5
},
// 选中时候显示内容
emphasis: {
label: {
color: 'black',
fontsize:15
},
itemStyle: {
// borderColor: '#00FFFF',
areaColor: '#ADFF2F'
}
},
roam: true,
data: this.dataList,
}
]
}
// 使用刚指定的配置项和数据显示图表。
myChart.setOption(option)
}
},
mounted() {
this.initChart()
}
}
</script>
<style scoped>
</style>
现在地图已经初步的有了样子,如果对地图显示上有个性化的的设置,可以自己去官网查看相关属性配置。
4.利用visualMap属性实现根据疫情数据动态的设置地图中各省市的颜色
// 视觉映射组件,根据疫情数据的不同,地图显示不同的颜色
visualMap: {
type: 'piecewise',
min: 0,
max: 1000,
left: 300,
bottom: 40,
showLabel: !0,
text: ['高', '低'],
pieces: [{
gt: 100,
label: '> 100 人',
color: '#7f1100',
symbol: 'roundRect'
}, {
gte: 10,
lte: 100,
label: '10 - 100 人',
color: '#ff5428'
}, {
gte: 1,
lt: 10,
label: '1 - 9 人',
color: '#ff8c71'
}, {
value: 0,
color: '#ADFF2F',
symbol: 'roundRect'
}],
show: !0
}
设置完这个属性,地图已经初步有了百度中国疫情的样子:
5.确诊为0的省市插上小旗子的标注
对比下百度做的疫情地图,清零的省地图上标注为小旗子,接下来实现这个功能:
小旗子这个样式百度了好久,看了好多案例,结果走错了路线,虽然也实现了该功能,但是特别繁琐,最后发现使用series-map中的markPoint属性可以轻松实现小旗子的功能:
(1)繁琐的方法思路:在中国地图坐标系中引入散点图(series-scatter),然后找到各个省份的坐标,通过该坐标布置散点图,并通过series-scatter的symbol改变散点的样式,自定义图例,设置小旗子的标注。该方法比较麻烦,而且对地图缩放设置也比较复杂,不太推荐。
(2)最终的思路:使用series-map中的markPoint属性可以轻松实现该功能,之后看api应该认真仔细,才能少走弯路。
在地图的series中添加markPoint属性,并做如下设置:
markPoint: { //标记点
// symbol设置的为小旗子图片的base64吗
symbol: 'image://data:image/png;base64,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',
symbolSize: 14, //图形大小
label: {
normal: {
formatter: function(params) {
console.log(params);
return params.name;
},
show: false,
},
emphasis: {
show: false,
}
},
data: this.markPointData
}
其中markPointData为需要标注小旗子的省份地图坐标数组,将清零之后的省份坐标放入markPointData中,该省份就会标注为小红旗。下面为操作的具体步骤:
a.从echarts中导入各省的坐标数据mapJson,对数据进行修整后存入geoCoordMap中,数据类型为{'新疆': [X值, Y值],'北京': [X值, Y值]...};
b.然后遍历存放疫情数据的变量dataList,当疫情数据value为0时,根据dataList的省份名称找出geoCoordMap对应的坐标,按markPointData的格式存放进去(南海诸岛有些特殊,详情看上方代码注释);
c.此时关于疫情的的数据都准备好了,此时可以初始化地图了(将生命周期函数呢的初始化地图操作this.initChart()去掉,直接调用this.getMapJson())。
import echarts from 'echarts'
import china from 'echarts/map/js/china'
import mapJson from 'echarts/map/json/china.json'
export default {
name: 'ChinaMap',
data() {
return {
dataList:[...], //疫情数据(上面写了,这里省略掉了。。)
markPointData: [ ], // 当某个省清零后,为这个省标注小红旗,存放这个省坐标的数据
geoCoordMap: { }, // 存放地图中各个省的坐标
}
}
}
methods:{
getMapJson() {
let mapXY = mapJson.features // 获取地图中各省的坐标数据
// 对获从echarts中获取的各个省的地图坐标进行修正
mapXY.forEach(res=>{
this.geoCoordMap[res.properties.name]=res.properties.cp
//旗子的位置跟省的名字重合了,纵坐标上移1.25
this.geoCoordMap[res.properties.name][1]=this.geoCoordMap[res.properties.name][1] + 1.25
})
// 对疫情数据进行遍历,查找出疫情数据为0的省份,加入到markPointData
this.dataList.forEach(res=>{
if(res.value===0) {
// echarts中未提供南海诸岛的坐标,此坐标为我自己根据坐标系推算的
if(res.name=== '南海诸岛') {
this.markPointData.push({
name: '南海诸岛',
coord: [130, 22],
value: 0
})
} else {
let tmp= {}
tmp.name= res.name
tmp.coord= this.geoCoordMap[res.name]
tmp.value= res.value
this.markPointData.push(tmp)
}
}
})
// 数据准备完毕后,初始化地图
this.initChart()
},
mounted() {
// this.initChart()
this.getMapJson()
}
}
6.最终仿百度中国疫情地图就实现了
补充:关于visualMap中当数据为0时,小旗子的显示,只需要添加symbol属性设置为小旗子的base64码,并设置其大小即可。