要实现的功能
比如:我们要实现白云地图24镇街的常住人口统计展示,然后需要我们实现 1s 自动轮播一次地区,自定义标签样式,自定义悬浮样式。
准备工作
1、安装依赖
npm i echarts -s
2、准备 24 镇街的 geoJson 数据
关于怎么获取 24 镇街的 geoJson 数据,请参考我之前的一篇博客:怎么获取echarts需要的geoJson数据去渲染地图:以广州市白云区24镇街为例
3、准备一份配置 24 镇街的文件
我们新建文件 440111.config.js
,在里面添加一些配置参数用于数据展示的方便,比如我配置了几个:
/** * 1)4个镇:江高镇、人和镇、太和镇、钟落潭镇; * 2)20个街:龙归街、大源街、三元里街、松洲街、景泰街、同德街、黄石街、棠景街、新市街、同和街、京溪街、永平街、嘉禾街、均禾街、石井街、金沙街、云城街、鹤龙街、白云湖街、石门街; * */ export const BAIYUN_CONFIG = [ '江高镇', '人和镇', '太和镇', '钟落潭镇', '龙归街', '大源街', '三元里街', '松洲街', '景泰街', '同德街', '黄石街', '棠景街', '新市街', '同和街', '京溪街', '永平街', '嘉禾街', '均禾街', '石井街', '金沙街', '云城街', '鹤龙街', '白云湖街', '石门街' ]; /** * SUCCESS_STYLE:绿色样式:'江高镇','太和镇','景泰街','黄石街','棠景街','京溪街','永平街','均禾街','石井街','金沙街'; * WARNING_STYLE:橙色样式:'人和镇','嘉禾街','三元里街','石门街','新市街','大源街','松洲街'; * ERROR_STYLE:红色样式:'钟落潭镇','同德街','云城街','鹤龙街','白云湖街','龙归街','同和街'; * */ export const SUCCESS_STYLE = ['江高镇','太和镇','景泰街','黄石街','棠景街','京溪街','永平街','均禾街','石井街','金沙街']; export const WARNING_STYLE = ['人和镇','嘉禾街','三元里街','石门街','新市街','大源街','松洲街']; export const ERROR_STYLE = ['钟落潭镇','同德街','云城街','鹤龙街','白云湖街','龙归街','同和街']; // 测试数据 export const MapData = [ { id: "jianggao", name: "江高镇", value: 883945 },{ id: "renhe", name: "人和镇", value: 4567992 },{ id: "taihe", name: "太和镇", value: 4567323 },{ id: "zhongluotan", name: "钟落潭镇", value: 497863 },{ id: "longgui", name: "龙归街", value: 3486257 },{ id: "dayuan", name: "大源街", value: 897435 },{ id: "sanyuanli", name: "三元里街", value: 46809544 },{ id: "songzhou", name: "松洲街", value: 123403 },{ id: "jingtai", name: "景泰街", value: 342256677 },{ id: "tongde", name: "同德街", value: 234677 },{ id: "huangshi", name: "黄石街", value: 976542 },{ id: "tangjing", name: "棠景街", value: 33456 },{ id: "xinshi", name: "新市街", value: 3455602 },{ id: "tonghe", name: "同和街", value: 487654 },{ id: "jingxi", name: "京溪街", value: 3876735 },{ id: "yongping", name: "永平街", value: 6677544 },{ id: "jiahe", name: "嘉禾街", value: 34526784 },{ id: "junhe", name: "均禾街", value: 8756534 },{ id: "shijing", name: "石井街", value: 220232 },{ id: "jinsha", name: "金沙街", value: 3352256 },{ id: "yuncheng", name: "云城街", value: 335677 },{ id: "helong", name: "鹤龙街", value: 334225 },{ id: "baiyunhu", name: "白云湖街", value: 34556 },{ id: "shimen", name: "石门街", value: 1354667 } ];
4、准备工具方法添加千分位
新建文件 utils.js
,添加千分位函数:
// 数字加千位分隔符 export function numToThsSprtr (num) { let res = num.toString().replace(/\d+/, function (n) { // 先提取整数部分 return n.replace(/(\d)(?=(\d{3})+$)/g, function ($1) { return $1 + ','; }); }) return res; }
代码实现
1、自定义标签
我们配置标签的相应样式,我们利用 formatter,以及 rich 去处理,具体可以参考:https://echarts.apache.org/zh/option.html#series-map.label.formatter,backgroundColor 可以使用背景图片。
label: { show: true, color: "#fff", textAlign: "center", // {a}:系列名。{b}:数据名。{c}:数据值。 formatter: (params) => { let richName = ""; if (SUCCESS_STYLE.includes(params.name)) { richName = "success"; } else if (WARNING_STYLE.includes(params.name)) { richName = "warning"; } else if (ERROR_STYLE.includes(params.name)) { richName = "error"; } return `{${richName}|${params.name}}`; }, rich: { success: { fontSize: "0.0729rem", padding: [4, 7], borderWidth: 2, // 图形描边的宽度。 borderColor: "#fff", // 边框颜色 backgroundColor: 'green', // 背景色 }, warning: { fontSize: "0.0729rem", padding: [4, 7], borderWidth: 2, // 图形描边的宽度。 borderColor: "#fff", // 边框颜色 backgroundColor: 'orange', // 背景色 }, error: { fontSize: "0.0729rem", padding: [4, 7], borderWidth: 2, // 图形描边的宽度。 borderColor: "#fff", // 边框颜色 backgroundColor: 'red', // 背景色 }, }, },
怎么处理标签名重叠问题?
如果大家出现下面这种标签名重叠的问题,那应该怎么去处理?
这里我们可以参考 github 上面 echarts 的问题 4379 进行相应的处理:中国地图,省份名称重叠 #4379
问题的描述:
两种方式处理重叠
第一种:在 geoJson 数据中添加 cp 属性数据。
"properties": { "name": "白云湖街", "cp": [113.23196411132812, 23.24386977767157] }
第二种:echarts.getMap(‘china’) 后修改已经加载的地图的数据。
var chinaMapInfoObj = document.getElementById(‘mianid‘’); var chinaMap = echarts.init(chinaMapInfoObj); var chinaEchartsObj = echarts.getMap('china'); var geoJSONChina = chinaEchartsObj.geoJson; var allDefProvince = geoJSONChina.features; for(var i=0,len=allDefProvince.length; i<len; i++){ var sglProvinceProperties = allDefProvince[i].properties; var sglProvinceName = sglProvinceProperties.name; switch(sglProvinceName){ case '新疆': sglProvinceProperties.cp[0]=87.617733; sglProvinceProperties.cp[1]=41.792818; break; case '青海'://def:101.778916,36.623178 sglProvinceProperties.cp[0]=97.617733; sglProvinceProperties.cp[1]=36.623178; break; case '江苏'://def:118.767413,32.041544 sglProvinceProperties.cp[0]=119.767413; sglProvinceProperties.cp[1]=33.041544; break; } } option.echarts.registerMap('china', geoJSONChina, {});
怎么确定中心点的坐标
可以去查看这个我写的这一篇博客里的确定边界汇聚点
部分,里面有介绍怎么处理。怎么获取 echarts 需要的 geoJson 数据去渲染地图:以广州市白云区24镇街为例
大致就是把矩形左上方的点放到新的位置中心,就可以得到大概的坐标。
得到坐标之后,可能有点不太精确,可以自己合适的调整数据,达到自己想要的位置就行。
比如:现在 松洲街
被 同德街
,挡住了,需要把它进行移动,
这里我们采用第一种方式,修改 geojson 数据,我们给松洲街添加 cp 坐标数据,我们发现就不会被遮住了,其他也是同样的原理
"properties": { "name": "松洲街", "cp": [113.21036999511719, 23.15851026498019] }
2、自定义悬浮提示
这里我们主要需要处理的就是 position 跟样式 formatter。这里的位置需要计算一下 (point:鼠标位置) 跟(contentSize:dom 的尺寸),找到合适的显示位置。具体的可以参考:
tooltip: { show: true, trigger: "item", // point:鼠标位置 contentSize:dom 的尺寸 position: (point, params, dom, rect, size) => { return [point[0] - 20, point[1] - size.contentSize[1] - 15]; }, extraCssText: "box-shadow: none", // 额外样式 formatter: (param) => { let data = ` <div class="map-tooltips"> <div class="name">${param.name}常住人口</div> <div class="value"> <span class="num">${numToThsSprtr(param.value || 0)}</span> <span class="unit">人</span> </div> </div> `; return data; }, },
3、自动轮播功能
我们可以通过 dispatchAction 实现轮播高亮提示效果。这里需要注意的就是轮播到最后一个时的状态处理。这里就不具体展开逻辑,看完整代码就行,有注释。
具体的配置参考:https://echarts.apache.org/zh/api.html#action
高亮指定的数据图形。
// 如果要高亮系列: dispatchAction({ type: 'highlight', // 用 index 或 id 或 name 来指定系列。 // 可以使用数组指定多个系列。 seriesIndex?: number | number[], seriesId?: string | string[], seriesName?: string | string[], // 数据项的 index,如果不指定也可以通过 name 属性根据名称指定数据项 dataIndex?: number | number[], // 可选,数据项名称,在有 dataIndex 的时候忽略 name?: string | string[], });
取消高亮指定的数据图形。
// 如果要取消高亮系列: dispatchAction({ type: 'downplay', // 用 index 或 id 或 name 来指定系列。 // 可以使用数组指定多个系列。 seriesIndex?: number | number[], seriesId?: string | string[], seriesName?: string | string[], // 数据项的 index,如果不指定也可以通过 name 属性根据名称指定数据项 dataIndex?: number | number[], // 可选,数据项名称,在有 dataIndex 的时候忽略 name?: string | string[], })
指定系列中的数据图形,根据 tooltip 的配置项显示提示框。
dispatchAction({ type: 'showTip', // 系列的 index,在 tooltip 的 trigger 为 axis 的时候可选。 seriesIndex?: number, // 数据项的 index,如果不指定也可以通过 name 属性根据名称指定数据项 dataIndex?: number, // 可选,数据项名称,在有 dataIndex 的时候忽略 name?: string,, // 本次显示 tooltip 的位置。只在本次 action 中生效。 // 缺省则使用 option 中定义的 tooltip 位置。 position: number[] | string | Function, })
隐藏提示框。
dispatchAction({ type: 'hideTip' })
4、大屏自适应方案
这个可以参考我的另外一篇:使用 sass + rem + flexible.js 实现大屏自适应
完整代码
<template> <div ref="geoTwoDimensionalMapChart" class="geo-two-dimensional-map-chart"></div> </template> <script> import * as echarts from "echarts"; // 白云区 geojson 数据 import baiyunGeoJson from "@/assets/mapData/440111.json"; // 24 镇街数据 import { BAIYUN_CONFIG, SUCCESS_STYLE, WARNING_STYLE, ERROR_STYLE, MapData } from "@/assets/mapData/440111.config.js"; // 工具方法 import { numToThsSprtr } from "@/utils/utils.js"; export default { name: "geoTwoDimensionalMapChart", data() { return { myChart: null, interval: 1000, // 时间间隔毫秒数 index: 0, // 播放所在下标 timer: null, option: { series: [{ type: "map", map: "白云区", data: [], layoutCenter: ["50%", "50%"], // 属性定义地图中心在屏幕中的位置 layoutSize: "99%", // 定义地图的大小 label: { show: true, color: "#fff", textAlign: "center", // {a}:系列名。{b}:数据名。{c}:数据值。 formatter: (params) => { let richName = ""; if (SUCCESS_STYLE.includes(params.name)) { richName = "success"; } else if (WARNING_STYLE.includes(params.name)) { richName = "warning"; } else if (ERROR_STYLE.includes(params.name)) { richName = "error"; } return `{${richName}|${params.name}}`; }, rich: { success: { fontSize: "0.0729rem", padding: [4, 7], borderWidth: 2, // 图形描边的宽度。 borderColor: "#fff", // 边框颜色 backgroundColor: 'green', // 背景色 }, warning: { fontSize: "0.0729rem", padding: [4, 7], borderWidth: 2, // 图形描边的宽度。 borderColor: "#fff", // 边框颜色 backgroundColor: 'orange', // 背景色 }, error: { fontSize: "0.0729rem", padding: [4, 7], borderWidth: 2, // 图形描边的宽度。 borderColor: "#fff", // 边框颜色 backgroundColor: 'red', // 背景色 }, }, }, itemStyle: { borderWidth: 2, // 图形描边的宽度。 borderColor: "#ddd", // 图形描边的颜色。 areaColor: '#ccc', shadowColor: '#eee', shadowBlur: 10, shadowOffsetX: 0, shadowOffsetY: 2 }, // 点击选择样式 select: { label: { color: "#fff", }, itemStyle: { borderWidth: 2, // 图形描边的宽度。 borderColor: "#ddd", // 图形描边的颜色。 areaColor: '#ccc', } }, emphasis: { // 当鼠标放上 label: { color: "#fff", }, itemStyle: { borderWidth: 2, borderColor: "#000", areaColor: 'blue', }, }, }], tooltip: { show: true, trigger: "item", // point:鼠标位置 contentSize:dom 的尺寸 position: (point, params, dom, rect, size) => { return [point[0] - 20, point[1] - size.contentSize[1] - 15]; }, extraCssText: "box-shadow: none", // 额外样式 formatter: (param) => { let data = ` <div class="map-tooltips"> <div class="name">${param.name}常住人口</div> <div class="value"> <span class="num">${numToThsSprtr(param.value || 0)}</span> <span class="unit">人</span> </div> </div> `; return data; }, }, }, }; }, mounted() { // 初始化渲染 this.initRender(); // 设置轮播 this.setIntervalMyChart(); // resize 事件监听 window.addEventListener("resize", this.handleResize); }, destroyed() { clearInterval(this.timer); window.removeEventListener("resize", this.handleResize); }, methods: { // 初始化渲染 initRender() { // 注册地图名字和数据 echarts.registerMap("白云区", baiyunGeoJson); let chartDom = this.$refs.geoTwoDimensionalMapChart; this.myChart = echarts.init(chartDom); this.option.series[0].data = MapData; this.myChart.setOption(this.option); // 激活高亮跟提示 console.log('initRender', this.index); this.dispatchActionChart("highlight", this.index); this.dispatchActionChart("showTip", this.index); // 鼠标划入 this.mouseEvents(); }, // 鼠标划入 mouseEvents() { this.myChart.on("mouseover", () => { // 停止定时器,清除之前的高亮 clearInterval(this.timer); this.dispatchActionChart("downplay", this.index); }); // 鼠标划出重新定时器开始 this.myChart.on("mouseout", () => { clearInterval(this.timer); // 启动轮播 this.setIntervalMyChart(); }); }, // 设置轮播 setIntervalMyChart() { const dataLength = BAIYUN_CONFIG.length; // 每隔 interval 进行一次切换 this.timer = setInterval(() => { // 清除高亮跟提示 this.dispatchActionChart("downplay", this.index); this.dispatchActionChart("hideTip", this.index); // 索引增加 this.index++; // 激活高亮跟提示 this.dispatchActionChart("highlight", this.index); this.dispatchActionChart("showTip", this.index); if (this.index === dataLength - 1) { // 需要对最后一个进行清除,对第一个进行激活 let tempTimer = setTimeout(() => { this.dispatchActionChart("downplay", dataLength - 1); this.dispatchActionChart("highlight", this.index); clearTimeout(tempTimer); }, this.interval); this.index = -1; } }, this.interval); }, /** * @description 控制高亮跟提示 * @param {String} type (highlight:高亮(反:downplay);showTip:显示提示(反:hideTip)) * @param {Number} dataIndex 数据项的 index * */ dispatchActionChart(type, dataIndex) { this.myChart.dispatchAction({ type: type, seriesIndex: 0, dataIndex: dataIndex, }); }, // resize 事件 handleResize() { this.myChart.resize(); }, }, }; </script> <style lang="scss" scoped> @import "@/assets/scss/utils.scss"; .geo-two-dimensional-map-chart { width: 100%; height: p2r(730); ::v-deep .map-tooltips { min-width: p2r(160); position: relative; text-align: center; padding-top: p2r(10); .name { font-size: p2r(16); font-weight: 400; color: green; line-height: p2r(22); margin-bottom: p2r(8); } .value { color: green; .num { font-size: p2r(26); font-weight: bold; line-height: p2r(26); } .unit { font-size: p2r(14); } } } } </style>
参考资料
echarts 图表行为 action
echarts 标签内容格式器series-map.label.formatter
echarts 提示框浮层的位置
echarts 提示框浮层内容格式器,支持字符串模板和回调函数两种形式
中国地图,省份名称重叠 #4379
怎么获取 echarts 需要的 geoJson 数据去渲染地图:以广州市白云区24镇街为例
使用 sass + rem + flexible.js 实现大屏自适应