Vue项目中使用Echarts绘制中国地图
本文基于Vue2+Echarts4实现中国地图的展示,将各个功能点以图文+代码的方式分步骤实现,希望对你能有所帮助~
·
写在前面
- 本文要介绍的是如何使用echarts来实现上图的可视化看板栗子,接下来我将以分步骤的方式将此功能逐步实现(希望足够细节…);
- 技术栈主要是 Echarts4 + vue2
- 在这之前我有写过一个Vue3+Ts+Websocket+Echarts5+Nodejs的完整栗子,这个例子除了地图还包括了一些常用的图表,例如饼图,折线图啥的;感兴趣的也可以看看,仓库地址传送门
1.准备一些静态资源
- 首要的就是地图的json或是js数据(没有的话可以进仓库复制一下,我放在了public下面的static文件夹里,你只需拷贝china.json即可);
- 可以根据自己喜好找一些背景图啥的,结合样式设置,可以让你的页面看起来更加酷炫;
2.页面布局
- 根据效果图,这里页面大体分为上下两部分,分为头部栏目和内容区域;
- 布局样式采用的 flex 布局;
- 相关的样式布局就不描述了,直接给代码,这里只介绍echarts相关的js逻辑代码。
3.渲染地图数据
3.1给一个容器并设置宽高
- 首先我们需要一个容器,这里给了一个div,类名就叫map;
- 宽高的话100%,也就是占满整个父容器;
3.2引入地图的json数据并渲染
- 通过import的方式导入json数据
- 接着在 mounted 函数中调用 echarts实例的 registerMap方法进行注册
- 而后自定义一些配置参数,并且通过 init 函数初始化地图
- 这里我通过
this.$echarts.xxx
方法的原因是我在入口文件main.js中把echarts实例挂载到了Vue的原型对象上,所以这么引用;
- 如果你没有这么做的话,可以在当前文件直接引入echarts,就像下面这样:
- 最后使用简单的三步就可以把地图渲染至页面了
3.3页面效果以及代码
- 代码
<template>
<div class="wrap">
<header class="header">
<div class="bg">
<img src="../../assets/imgs/header_border_dark.png" alt="" />
</div>
<div class="title">北极星的看板栗子</div>
<div class="time">
<span>当前时间:</span>
<span> 2024年1月15日 9:50 </span>
</div>
</header>
<div class="body">
<section class="left">
<div class="title">▣ 选择地区</div>
<div class="map" ref="mapRef"></div>
</section>
<section class="right">
<div class="title">
<span class="desc"
>各种动物数量排名列表 <span class="order"> TOP10</span></span
>
</div>
<div class="list">
</div>
</section>
</div>
</div>
</template>
<script>
import chinaJson from '/public/static/map/china.json';
import cloneDeep from 'lodash/cloneDeep';
export default {
data() {
return {
chartRef: null,
};
},
mounted() {
this.$echarts.registerMap('china', chinaJson);
this.initChart();
},
methods: {
initChart() {
const option = {
tooltip: {
//提示框组件
show: true,
trigger: 'item'
},
series: [
{
name: '中国',
type: 'map',
map: 'china',
left: '10%', //移动地图在容器中的位置
top: '8%',
label: {
show: true,
color: '#fff',
fontSize: 10
},
itemStyle: {
areaColor: '#151d4c',
borderWidth: 1, // 区域边框宽度,默认为1,表示边框宽度为1px
borderColor: '#000' // 区域边框颜色值,默认为#e1e1e1,表示浅灰色边框
},
emphasis: {
// 选中状态下的样式设置
label: {
// 标签样式设置
show: true
},
itemStyle: {
// 区域填充样式设置
areaColor: '#90c31d'
}
},
}
]
};
this.chartRef = this.$echarts.init(this.$refs.mapRef);
this.chartRef.setOption(option);
}
}
};
</script>
<style lang="less" scoped>
.wrap {
width: 100%;
height: 100%;
background-color: #152f5b;
.header {
width: 100%;
height: 12%;
// background-color: rgb(34, 32, 30);
position: relative;
.bg {
width: 100%;
position: absolute;
img {
width: 100%;
}
}
.title {
position: absolute;
left: 50%;
transform: translateX(-50%);
color: #fff;
font-size: 20px;
top: 15%;
}
.time {
position: absolute;
left: 2%;
bottom: 0;
color: #fff;
font-size: 20px;
}
}
.body {
width: 100%;
height: 87%;
display: flex;
.left {
width: 59%;
margin-right: 10px;
height: 100%;
border: 1px salmon solid;
// background-color: rgb(136, 182, 182);
position: relative;
.title {
position: absolute;
width: 80%;
height: 50px;
line-height: 50px;
color: #fff;
padding-left: 20px;
}
.map {
height: 100%;
width: 100%;
}
}
.right {
width: 40%;
height: 100%;
border: 1px #ff9f43 solid;
.title {
width: 60%;
height: 50px;
line-height: 50px;
// border: 1px solid seagreen;
// padding-left: 20px;
color: #ff9f43;
font-weight: 600;
.desc {
text-shadow: 2px 2px 4px #ff9f43;
text-decoration: underline #2ccedf 2px;
text-decoration-skip: none;
}
}
.list {
color: #fff;
background-color: #0a06069e;
width: 100%;
}
}
}
}
</style>
4.自定义不同区块颜色
- 在上面的步骤我们已经把地图渲染出来了,但是每个区块的颜色完全一样
- 为了美观我们可以自行定义每个区块的颜色
- 这里我们通过设置 visualMap参数来实现,具体配置在下面截图说明,如下:
- 这时候再看页面的效果
- 代码
data() {
return {
// 新增省区数据源
provinceList: [
{ name: '吉林', value: 36, pos: [125.8154, 44.2584] },
{ name: '北京', value: 32, pos: [116.4551, 40.2539] },
{ name: '辽宁', value: 54, pos: [123.1238, 42.1216] },
{ name: '河北', value: 23, pos: [114.4995, 38.1006] },
{ name: '天津', value: 78, pos: [117.4219, 39.4189] },
{ name: '山西', value: 30, pos: [112.3352, 37.9413] },
{ name: '陕西', value: 50, pos: [109.1162, 34.2004] },
{ name: '甘肃', value: 80, pos: [103.5901, 36.3043] },
{ name: '宁夏', value: 20, pos: [106.3586, 38.1775] },
{ name: '四川', value: 60, pos: [103.9526, 30.7617] },
{ name: '山东', value: 30, pos: [117.1582, 36.8701] },
{ name: '河南', value: 24, pos: [113.4668, 34.6234] },
{ name: '江苏', value: 72, pos: [118.8062, 31.9208] },
{ name: '湖北', value: 90, pos: [114.3896, 30.6628] },
{ name: '浙江', value: 30, pos: [119.5313, 29.8773] },
{ name: '福建', value: 40, pos: [119.4543, 25.9222] },
{ name: '江西', value: 20, pos: [116.0046, 28.6633] },
{ name: '湖南', value: 80, pos: [113.0823, 28.2568] },
{ name: '贵州', value: 30, pos: [106.6992, 26.7682] },
{ name: '云南', value: 10, pos: [102.9199, 25.4663] },
{ name: '海南', value: 20, pos: [110.3893, 19.8516] },
{ name: '上海', value: 82, pos: [121.4648, 31.2891] },
{ name: '香港', value: 60, pos: [114.3, 22.9] },
{ name: '澳门', value: 30, pos: [113.5, 22.2] },
{ name: '台湾', value: 70, pos: [121, 23] },
{ name: '广东', value: 85, pos: [113.12244, 23.009505] },
{ name: '安徽', value: 59, pos: [117.29, 32.0581] },
{ name: '广西', value: 70, pos: [108.479, 23.1152] },
{ name: '青海', value: 30, pos: [99.4038, 36.8207] },
{ name: '新疆', value: 30, pos: [87.9236, 43.5883] },
{ name: '西藏', value: 80, pos: [88.388277, 31.56375] },
{ name: '重庆', value: 70, pos: [108.384366, 30.439702] },
{ name: '黑龙江', value: 70, pos: [127, 48] },
{ name: '内蒙古', value: 30, pos: [110.3467, 41.4899] }
],
};
},
methods: {
// 修改初始化图表函数
initChart() {
const option = {
// 新增此配置
visualMap: {
textStyle: {
color: '#fff'
},
show: true, // 是否显示视觉映射组件
min: 0, // 视觉映射组件的最小值
max: 100, // 视觉映射组件的最大值
left: '2%', // 视觉映射组件的左边界
bottom: '12%', // 视觉映射组件的上边界
calculable: true, // 是否开启视觉映射组件的拖拽缩放功能
inRange: {
// 视觉映射组件的色彩映射区间
// 不同区间的颜色值
color: [
'#4c78da',
'#514aca',
'#4243c2',
'#4d32a5',
'#3892dc',
'#5f1e83',
'#25abde',
'#3497dc',
'#dfc32d'
]
}
},
series: [
{
// 新增此属性
data: this.provinceList
}
]
};
}
5.高亮选中的区域
- 在单击省区的时候希望高亮此区块,一是能够保留上次选中的效果,二是后续可以通过此操作获取不同省区的数据,例如点击之后调用接口获取不同数据展示在右侧;
- 这里核心方法是通过 echarts实例上的on方法去监听点击操作;
- 新增一个linstenProvinceClick函数,在点击之后将该省份的value值设置为100 (100在当前栗子中对应的就是黄色),而后通过 setOption 方法重新渲染图表;
- setOption这个方法类似于 Object.assign方法,他会将配置进行合并而不是覆盖,并且允许多次调用;
- 代码
linstenProvinceClick() {
// 接收一个对象, 结构出的data对象值为series数组中data数据源中的对象
this.chartRef.on('click', ({ data }) => {
const provinceName = data.name;
// cloneDeep 是lodash 库的方法,深拷贝一份数组,避免对原始数据造成影响
const provinceList = cloneDeep(this.provinceList);
provinceList?.forEach(item => {
if (item.name === provinceName) {
item.value = 100;
}
});
const option = {
series: [
{
data: provinceList
}
]
};
this.chartRef.setOption(option);
});
}
- 这时候回到页面可以看到,点击过的区域变成了黄色,绿色是原本设置的鼠标滑过的颜色;
6.根据页面窗口变化使图表自适应
● echarts实例提供 resize方法,调用时可以改变图表的尺寸
● 在组件中监听窗口的变化调用 resize方法
- 在浏览器看看效果,可以发现当窗口缩小时,图表也自动缩放了
- 代码
beforeDestroy() {
// 组件销毁前取消监听事件
window.removeEventListener(this.adaptScreen);
},
methods: {
initChart() {
// 新增监听
window.addEventListener('resize', this.adaptScreen);
},
adaptScreen() {
this.chartRef.resize();
}
}
7.右侧添加列表数据展示看板
● 这个根据自己需求是否添加;
● 表格组件这里没有使用element-ui的 el-table 组件,而是使用原生的table,这样方便自行定义样式;否则需要使用 属性透传的方式去修改el-table的默认样式,视自己情况而定;
● 最终效果就如下
8.最终代码
<template>
<div class="wrap">
<header class="header">
<div class="bg">
<img src="../../assets/imgs/header_border_dark.png" alt="" />
</div>
<div class="title">北极星的看板栗子</div>
<div class="time">
<span>当前时间:</span>
<span> 2024年1月15日 9:50 </span>
</div>
</header>
<div class="body">
<section class="left">
<div class="title">▣ 选择地区</div>
<div class="map" ref="mapRef"></div>
</section>
<section class="right">
<div class="title">
<span class="desc"
>各种动物数量排名列表 <span class="order"> TOP10</span></span
>
</div>
<div class="list">
<table class="table" cellspacing="0" border="1">
<thead>
<tr>
<th width="10%">排名</th>
<th width="15%">动物名称</th>
<th width="15%">来自地区</th>
<th width="23%">增长率</th>
<th width="15%">总数量</th>
<th width="22%">死亡率</th>
</tr>
</thead>
<tbody>
<tr>
<td align="center">1</td>
<td align="center">雄狮院长</td>
<td align="center">大草原</td>
<td align="center">20%</td>
<td align="center">18895</td>
<td align="center">15%</td>
</tr>
</tbody>
</table>
</div>
</section>
</div>
</div>
</template>
<script>
import chinaJson from '/public/static/map/china.json';
import cloneDeep from 'lodash/cloneDeep'; //lodash库中深拷贝对象的方法
export default {
data() {
return {
chartRef: null,//echarts初始化的实例对象
provinceList: [
{ name: '吉林', value: 36, pos: [125.8154, 44.2584] },
{ name: '北京', value: 32, pos: [116.4551, 40.2539] },
{ name: '辽宁', value: 54, pos: [123.1238, 42.1216] },
{ name: '河北', value: 23, pos: [114.4995, 38.1006] },
{ name: '天津', value: 78, pos: [117.4219, 39.4189] },
{ name: '山西', value: 30, pos: [112.3352, 37.9413] },
{ name: '陕西', value: 50, pos: [109.1162, 34.2004] },
{ name: '甘肃', value: 80, pos: [103.5901, 36.3043] },
{ name: '宁夏', value: 20, pos: [106.3586, 38.1775] },
{ name: '四川', value: 60, pos: [103.9526, 30.7617] },
{ name: '山东', value: 30, pos: [117.1582, 36.8701] },
{ name: '河南', value: 24, pos: [113.4668, 34.6234] },
{ name: '江苏', value: 72, pos: [118.8062, 31.9208] },
{ name: '湖北', value: 90, pos: [114.3896, 30.6628] },
{ name: '浙江', value: 30, pos: [119.5313, 29.8773] },
{ name: '福建', value: 40, pos: [119.4543, 25.9222] },
{ name: '江西', value: 20, pos: [116.0046, 28.6633] },
{ name: '湖南', value: 80, pos: [113.0823, 28.2568] },
{ name: '贵州', value: 30, pos: [106.6992, 26.7682] },
{ name: '云南', value: 10, pos: [102.9199, 25.4663] },
{ name: '海南', value: 20, pos: [110.3893, 19.8516] },
{ name: '上海', value: 82, pos: [121.4648, 31.2891] },
{ name: '香港', value: 60, pos: [114.3, 22.9] },
{ name: '澳门', value: 30, pos: [113.5, 22.2] },
{ name: '台湾', value: 70, pos: [121, 23] },
{ name: '广东', value: 85, pos: [113.12244, 23.009505] },
{ name: '安徽', value: 59, pos: [117.29, 32.0581] },
{ name: '广西', value: 70, pos: [108.479, 23.1152] },
{ name: '青海', value: 30, pos: [99.4038, 36.8207] },
{ name: '新疆', value: 30, pos: [87.9236, 43.5883] },
{ name: '西藏', value: 80, pos: [88.388277, 31.56375] },
{ name: '重庆', value: 70, pos: [108.384366, 30.439702] },
{ name: '黑龙江', value: 70, pos: [127, 48] },
{ name: '内蒙古', value: 30, pos: [110.3467, 41.4899] }
]
};
},
mounted() {
// 名称必须是 china ,否则南海岛屿无法展示
this.$echarts.registerMap('china', chinaJson);
this.initChart();
},
beforeDestroy() {
window.removeEventListener(this.adaptScreen);
},
methods: {
initChart() {
const option = {
tooltip: {
//提示框组件
show: true,
trigger: 'item'
},
visualMap: {
textStyle: {
color: '#fff'
},
show: true, // 是否显示视觉映射组件
min: 0, // 视觉映射组件的最小值
max: 100, // 视觉映射组件的最大值
left: '2%', // 视觉映射组件的左边界
bottom: '12%', // 视觉映射组件的上边界
calculable: true, // 是否开启视觉映射组件的拖拽缩放功能
inRange: {
// 视觉映射组件的色彩映射区间
// 不同区间的颜色值
color: [
'#4c78da',
'#514aca',
'#4243c2',
'#4d32a5',
'#3892dc',
'#5f1e83',
'#25abde',
'#3497dc',
'#dfc32d'
]
}
},
series: [
{
name: '中国',
type: 'map',
map: 'china',
left: '10%', //移动地图在容器中的位置
top: '8%',
label: {
show: true,
color: '#fff',
fontSize: 10
},
itemStyle: {
areaColor: '#151d4c',
borderWidth: 1, // 区域边框宽度,默认为1,表示边框宽度为1px
borderColor: '#000' // 区域边框颜色值,默认为#e1e1e1,表示浅灰色边框
},
emphasis: {
// 选中状态下的样式设置
label: {
// 标签样式设置
show: true
},
itemStyle: {
// 区域填充样式设置
areaColor: '#90c31d'
}
},
data: this.provinceList
}
]
};
this.chartRef = this.$echarts.init(this.$refs.mapRef);
this.linstenProvinceClick();
this.chartRef.setOption(option);
window.addEventListener('resize', this.adaptScreen);
},
adaptScreen() {
this.chartRef.resize();
},
linstenProvinceClick() {
// 接收一个对象, 解构出的data对象值为series数组中data数据源中的对象
this.chartRef.on('click', ({ data }) => {
if (!data || !data.name) return;
const provinceName = data.name;
const provinceList = cloneDeep(this.provinceList);
provinceList?.forEach(item => {
if (item.name === provinceName) {
item.value = 100;
}
});
const option = {
series: [
{
data: provinceList
}
]
};
this.chartRef.setOption(option);
});
}
}
};
</script>
<style lang="less" scoped>
.wrap {
width: 100%;
height: 100%;
background-color: #152f5b;
.header {
width: 100%;
height: 12%;
// background-color: rgb(34, 32, 30);
position: relative;
.bg {
width: 100%;
position: absolute;
img {
width: 100%;
}
}
.title {
position: absolute;
left: 50%;
transform: translateX(-50%);
color: #fff;
font-size: 20px;
top: 15%;
}
.time {
position: absolute;
left: 2%;
bottom: 0;
color: #fff;
font-size: 20px;
}
}
.body {
width: 100%;
height: 87%;
display: flex;
.left {
width: 59%;
margin-right: 10px;
height: 100%;
position: relative;
.title {
position: absolute;
width: 80%;
height: 50px;
line-height: 50px;
color: #fff;
padding-left: 20px;
}
.map {
height: 100%;
width: 100%;
}
}
.right {
width: 40%;
height: 100%;
.title {
width: 60%;
height: 50px;
line-height: 50px;
color: #ff9f43;
font-weight: 600;
.desc {
text-shadow: 2px 2px 4px #ff9f43;
text-decoration: underline #2ccedf 2px;
text-decoration-skip: none;
}
}
.list {
color: #fff;
background-color: #0a06069e;
width: 100%;
table {
width: 100%;
thead {
th {
font-weight: normal;
height: 30px;
}
}
td {
border-top: 1px solid #2ccedf;
height: 25px;
}
}
}
}
}
}
</style>
写在最后
- 以上就是本文的全部内容了,希望这篇文章能对你在使用echarts渲染地图的功能实现上有所帮助;
- 另外,如果本文对你有所帮助或者有所启发的话,可以点个赞鼓励一下,感谢你这么优秀还能看我的文章;
- 仓库地址放这了,传送门~~
开放原子开发者工作坊旨在鼓励更多人参与开源活动,与志同道合的开发者们相互交流开发经验、分享开发心得、获取前沿技术趋势。工作坊有多种形式的开发者活动,如meetup、训练营等,主打技术交流,干货满满,真诚地邀请各位开发者共同参与!
更多推荐
已为社区贡献2条内容
所有评论(0)