如图,需求要做一个在地图上显示柱状图的echarts图,但是百度了半天,发现很少有人发这种例子。这个代码也是借鉴的别人的文章,但需求肯定不完全一致,那我会根据我的需求把代码和注意事项发出来并解释。(如果有人发现了借鉴的那篇,请在评论区发一下链接,我找不到了,谢谢)
话不多说,先上代码
目录:
index.vue文件(也就是组件)
<template>
<div class="ChinaEcharts">
<div class="wrap">
<div id="map" :style="'width:' + Width + ';height:' + Height">
<div :style="'width:' + Width + ';height:' + Height" ref="myEchart"></div>
</div>
</div>
</div>
</template>
<script>
import * as echarts from 'echarts'
import tangshan from "./tangshan.json";
import Options from "./optionConfig"; //地图配置
export default {
name: "ChinaEcharts",
props: {
Width: {
type: String,
default() {
return '100%';
},
},
Height: {
type: String,
default() {
return '360px';
},
},
areaData: {
type: Array,
default() {
return [];
},
},
option: {
type: Object,
default() {
return new Options();
},
},
cityConfig: {
type: Object,
default() {
return {};
},
},
areaItems: {
type: Object,
default() {
return {};
},
},
},
data() {
return {
wrap: null, //包裹框
drawBar: null, // 柱状图
barWrap: null,
myChart: null,
// 地区坐标
selfAreaItems: {},
selfAreaData: [],
selfOption: new Options(),
barColor: ''
};
},
watch: {
areaData: {
deep: true,
handler: function(newVal,oldVal) {
this.initEcharts();
}
}
},
methods: {
loadMap(mapName, data) {
if (data) {
console.log(mapName, data, "mapName, datamapName, data");
this.echarts.registerMap(mapName, data);
}
},
initEcharts() {
this.echarts.registerMap("map", tangshan);
this.selfAreaData = JSON.parse(JSON.stringify(this.areaData));
this.selfOption = JSON.parse(JSON.stringify(this.option));
this.myChart = this.echarts.init(this.$refs.myEchart);
if (this.cityConfig.dataJson) {
this.loadMap(this.cityConfig.name, this.cityConfig.dataJson);
}
this.selfAreaItems = JSON.parse(JSON.stringify(this.areaItems));
window.onresize = this.myChart.resize;
this.myChart.setOption(this.selfOption);
// 拖拽跟缩放重置
let throttledRenderEachCity = this.throttle(this.renderItems, 0);
this.myChart.on("geoRoam", throttledRenderEachCity);
this.renderItems();
},
// 缩放和拖拽
throttle(fn, delay, debounce) {
let currCall;
let lastCall = 0;
let lastExec = 0;
let timer = null;
let diff;
let scope;
let args;
delay = delay || 0;
function exec() {
lastExec = new Date().getTime();
timer = null;
fn.apply(scope, args || []);
}
let cb = function () {
currCall = new Date().getTime();
scope = this;
args = arguments;
diff = currCall - (debounce ? lastCall : lastExec) - delay;
clearTimeout(timer);
if (debounce) {
timer = setTimeout(exec, delay);
} else {
if (diff >= 0) {
exec();
} else {
timer = setTimeout(exec, -diff);
}
}
lastCall = currCall;
};
return cb;
},
// 填充 地图点位
renderItems() {
let option = Object.assign(this.selfOption, {
xAxis: [],
yAxis: [],
grid: [],
series: [],
tooltip: {
trigger: "item",
axisPointer: {
type: "none",
},
},
});
this.selfAreaData.forEach((item, idx) => {
let nodeCoord = this.selfAreaItems[item.areaName];
let coord = this.myChart.convertToPixel("geo", nodeCoord);
let itemData = []
itemData.push(Number(item.value));
if(item.value >= 0 && item.value<=5) {
this.barColor = '#58dabd';
}else if(item.value > 5 && item.value<=10) {
this.barColor = '#fec018';
}else if(item.value > 10) {
this.barColor = '#f75218';
}
if (coord) {
option.xAxis.push({
id: idx + item.areaName,
gridId: idx + item.areaName,
type: "category",
// name: item.areaName,//是否显示柱状图文字
nameLocation: "middle",
nameGap: 3,
splitLine: {
show: false,
},
axisTick: {
show: false,
},
axisLabel: {
show: false,
},
axisLine: {
show: false,
},
// data: titleItems,
z: 100,
});
option.yAxis.push({
id: idx + item.areaName,
gridId: idx + item.areaName,
type: "value",
splitLine: {
show: false,
},
axisTick: {
show: false,
},
axisLabel: {
show: false,
},
axisLine: {
show: false,
lineStyle: {
color: "red",
},
},
min: 0,
max: "dataMax",
});
option.grid.push({
id: idx + item.areaName,
width: 15,
height: 30,
left: coord[0] - 15,
top: coord[1] - 15,
z: 10,
});
option.series.push({
id: idx + item.areaName,
type: "bar",
xAxisId: idx + item.areaName,
yAxisId: idx + item.areaName,
barGap: 0,
barCategoryGap: 0,
data: itemData,
barWidth: 8,
z: 100,
itemStyle: {
normal: {
color: new echarts.graphic.LinearGradient(0, 1, 0, 0, [{
// 四个数字分别对应 数组中颜色的开始位置,分别为 右,下,左,上。例如(1,0,0,0 )代表从右边开始渐
// 变。offset取值为0~1,0代表开始时的颜色,1代表结束时的颜色,柱子表现为这两种颜色的渐变。
offset: 0,
color: '#ffffff'
}, {
offset: 1,
color: this.barColor
}]),
},
opacity: 0,
emphasis: {
label: {
show: false,
},
},
},
});
}
});
this.myChart.setOption(option);
},
},
};
</script>
optionConfig.js文件(这个是地图的配置项)
export default class Options {
constructor() {
return {
tooltip: {},
geo: {
map: "map",
roam: true,
label: {
show: false,
normal: {
show: true,
textStyle: {
color: "#8483c0",
fontSize: 12,
},
},
emphasis: {
show: true,
textStyle: {
color: "black",
fontSize: 12,
},
},
},
itemStyle: {
// 普通样式。
normal: {
areaColor: '#fffefe',//背景颜色
borderWidth: 2, //设置外层边框
borderColor: '#8483c0'
},
emphasis: {// 也是选中样式
areaColor: "#8483c0",
shadowOffsetX: 0,
shadowOffsetY: 0,
shadowBlur: 20,
borderWidth: 0,
shadowColor: "rgba(0, 0, 0, 0.5)",
}
},
clickable:false,
},
visualMap: {
type:'piecewise',
showLabel:true,
pieces:[
{min: 10,label:'>10'},
{min: 6, max: 10,label:'5-10'},
{min: 0, max: 5,label:'0-5'},
],
inRange: {
color: ['#56dabb','#fec52c','#f7551c']
},
seriesIndex: 999,
left:35,
bottom:35,
textStyle: {
color: "#8483c0",
}
},
};
}
}
tangshan.json文件(可以到下面的链接下载需要的地图json,下载的格式应该是geoJson,改成json文件就行了)
echarts-map最新实时geoJson文件下载_hxkj.vip_HashTang
组件的引入:
import chinaEcharts from '../components/ChinaEcharts/index.vue'
<china-echarts :areaData="areaData" :areaItems="areaItems"></china-echarts>
areaData 是数组格式是
areaData: [
{
areaName:'路南区',
value: 9
},
{
areaName:'路北区',
value: 19
},
{
areaName:'遵化市',
value: 2
},
{
areaName:'迁西县',
value: 5
},
{
areaName:'丰南区',
value: 10
},
],
areaItems 因为每个柱状图是遍历的 所以这个传入的配置项是地区对应的柱状图坐标(这个坐标可以在json文件找,我图省事就是用的center坐标,如果有更好的方法请告知我谢谢)
代码放完了,接下来改说说注意事项是什么了
第一个注意点(最重要!!!):调后台接口有数据但是没有图
我们柱状图的数据肯定是要调后台接口的。但是会发现我们用假的数据,能正常显示柱状图,但是调了后台接口就显示不出来了。在模拟的数据和处理返回的数据格式都一致的情况下。那就是渲染的问题了。
我们请求是异步的,所以客户端就没等服务端相应完就渲染了。所以会导致柱状图再渲染的时候,你传入的数据还是空的
解决办法:
用watch监听数据变化 在渲染柱状图(我这个是封装的组件,如果是在同一个页面就直接在请求接口的方法里面调用initEcharts方法就行了)。用假数据测试,initEcharts方法写在mounted里面就行。
watch: {
areaData: {
deep: true,
handler: function(newVal,oldVal) {
this.initEcharts();
}
}
},
第二个注意点:柱状图渐变色的问题
我的需求是根据传入的不同数值,显示不同的颜色,在index文件里面 renderItems()里面是写柱状图配置的。
在series里面的itemStyle的normal 写color的配置
renderItems ()里面写对颜色的判断
color: new echarts.graphic.LinearGradient(0, 1, 0, 0, [{
// 四个数字分别对应 数组中颜色的开始位置,分别为 右,下,左,上。例如(1,0,0,0 )代表从右边开始渐
// 变。offset取值为0~1,0代表开始时的颜色,1代表结束时的颜色,柱子表现为这两种颜色的渐变。
offset: 0,
color: '#ffffff'
}, {
offset: 1,
color: this.barColor
]),
注意:offset 中间也可以加0.5,这样就可以三个颜色过渡了。
第三个注意点:visualMap会覆盖柱状图的渐变色
echarts里面地图的示例配置是visualMap 不是 lengend
导致会覆盖柱状图颜色的罪魁祸首就是seriesIndex,echarts官网说的很清楚了,因为我这块只是为了展示用,不做任何功能,所以就不用关联 设一个不可能的值就行了,这样就不会覆盖改好的柱状图颜色了。
第四个注意点:一个地区渲染多个柱状条和名字
在index文件中,传入的areaData里面的areaName就是每个柱状图的名字,不过我不需要,需要的把他打开就行了
如果在一个地区渲染多个柱状条的话,itemData也就是areaData里面的value,这是一个数组,如果传入的数组长度是2,那就在一个区域会显示两个柱条,并且有高低变化。
第五个注意点:echarts引入方式
import * as echarts from 'echarts'
不管在哪引入echarts,要用* as的形式,官方推荐用法,直接用import from 可能报echarts undefined的错
补充 :解决每个区域柱状图高度一直问题
这个问题真的想了很久,甚至想过在数组里面加一个value,然后只显示一个柱条。但没想到咋改。突然茅塞顿开,既然让他给高度不一致,直接设置y轴刻度不就行了。一开始想的是把每组的最大值算出来赋给y轴刻度。然后想了一下不太好,如果数据很多总和加起来就很多了,那少的数据不就几乎看不见了。所以要算每个区域的最大值赋给y轴,话不多说,上代码
在initEcharts方法里面获取最大值
// 遍历数组获取最大值
for(let i = 0;i<this.selfAreaData.length;i++) {
if(this.maxValue<this.selfAreaData[i].value) {
this.maxValue = this.selfAreaData[i].value
}
}
或者换一种更简便的方法,用reduce函数一行代码
this.maxValue = this.selfAreaData.reduce((pre, cur) => pre > cur?.value ? pre : cur?.value)
data里面定义一下maxValue
在柱状图配置项里面的yAxis里面的max属性设置刻度就搞定了
最后附效果图(柱条的样式可以根据需要修改)
如有问题,请指出,谢谢!