效果图:
1. 高德地图 Key 申请
2. 引入使用AMapJS
帮助开发者快速加载高德地图相关API,在模块化应用、异步编程中使用API更加灵活便捷。
安装:
npm i amap-js -S
完整引入:
import AMapJS from 'amap-js';
本文仅使用AMapUILoader
3. Vue 中实现效果图完整代码
<template> <div class="app-container"> <div id="chinaMap"></div> <div class="mapChoose"> <span v-for="(item,index) in parentInfo" :key="item.code"> <span class="title" @click="chooseArea(item,index)" >{{item.cityName=='全国'?'中国':item.cityName}}</span> <span class="icon" v-show="index+1!=parentInfo.length">></span> </span> </div> </div> </template> <script> import AMapJS from 'amap-js' import resize from '@/mixins/resize' export default { name: 'home', components: {}, data() { return { // 创建AMapJSAPI Loader amapLoader: new AMapJS.AMapLoader({ key: '申请的高德地图key', version: '1.4.15', plugins: [], }), // 创建AMapUI Loader amapuiLoader: new AMapJS.AMapUILoader({ version: '1.0' }), timeTitle: ['2016', '2017', '2018', '2019', '2020'], parentInfo: [ { cityName: '全国', code: 100000, }, ], geoJson: { features: [], }, } }, mixins: [resize], methods: { async getGeoJson(adcode) { await this.amapLoader.load() await this.amapuiLoader.load() let that = this AMapUI.loadUI(['geo/DistrictExplorer'], DistrictExplorer => { var districtExplorer = new DistrictExplorer() districtExplorer.loadAreaNode(adcode, function(error, areaNode) { if (error) { console.error(error) return } let Json = areaNode.getSubFeatures() if (Json.length > 0) { that.geoJson.features = Json } else if (Json.length === 0) { that.geoJson.features = that.geoJson.features.filter( item => item.properties.adcode == adcode ) if (that.geoJson.features.length === 0) return } that.getMapData() }) }) }, // 获取数据 getMapData() { let mapData = {}, pointData = {}, sum = {} for (let i = 0; i < this.timeTitle.length; i++) { mapData[this.timeTitle[i]] = [] pointData[this.timeTitle[i]] = [] sum[this.timeTitle[i]] = 0 for (let j = 0; j < this.geoJson.features.length; j++) { let value = Math.random() * 3000 mapData[this.timeTitle[i]].push({ name: this.geoJson.features[j].properties.name, value: value, level: this.geoJson.features[j].properties.level, cityCode: this.geoJson.features[j].properties.adcode, }) pointData[this.timeTitle[i]].push({ name: this.geoJson.features[j].properties.name, value: [ this.geoJson.features[j].properties.center[0], this.geoJson.features[j].properties.center[1], value, ], cityCode: this.geoJson.features[j].properties.adcode, }) sum[this.timeTitle[i]] += value } mapData[this.timeTitle[i]] = mapData[this.timeTitle[i]].sort(function( a, b ) { return b.value - a.value }) } this.drawChinaMap(mapData, pointData, sum) }, drawChinaMap(mapData, pointData, sum) { var echarts = require('echarts') var mapChart = echarts.init(document.getElementById('chinaMap')) //这里做个切换,全国的时候才显示南海诸岛 只有当注册的名字为china的时候才会显示南海诸岛 if (this.parentInfo.length === 1) { echarts.registerMap('china', this.geoJson) //注册 } else { echarts.registerMap('map', this.geoJson) //注册 } var option = { timeline: { data: this.timeTitle, axisType: 'category', autoPlay: true, playInterval: 3000, left: '10%', right: '10%', bottom: '2%', width: '80%', label: { normal: { textStyle: { color: 'rgb(179, 239, 255)', }, }, emphasis: { textStyle: { color: '#fff', }, }, }, symbolSize: 10, lineStyle: { color: '#8df4f4', }, checkpointStyle: { borderColor: '#8df4f4', color: '#53D9FF', borderWidth: 2, }, controlStyle: { showNextBtn: true, showPrevBtn: true, normal: { color: '#53D9FF', borderColor: '#53D9FF', }, emphasis: { color: 'rgb(58,115,192)', borderColor: 'rgb(58,115,192)', }, }, }, baseOption: { animation: true, animationDuration: 900, animationEasing: 'cubicInOut', animationDurationUpdate: 900, animationEasingUpdate: 'cubicInOut', tooltip: { trigger: 'axis', axisPointer: { type: 'shadow', }, }, grid: { right: '2%', top: '12%', bottom: '8%', width: '20%', }, toolbox: { feature: { restore: { show: false, }, dataView: { optionToContent: function(opt) { let series = opt.series[0].data //折线图数据 //表头 let tdHeads = '<th style="padding: 0 20px">所在地区</th><th style="padding: 0 20px">销售额</th>' let tdBodys = '' //数据 let table = `<table border="1" style="margin-left:20px;border-collapse:collapse; font-size:14px;text-align:left;"><tbody><tr>${tdHeads} </tr>` for (let i = 0; i < series.length; i++) { table += `<tr> <td style="padding: 0 50px">${series[i].name}</td> <td style="padding: 0 50px">${series[ i ].value.toFixed(2)}万</td> </tr>` } table += '</tbody></table>' return table }, }, saveAsImage: { name: this.parentInfo[this.parentInfo.length - 1].cityName + '销售额统计图', }, dataZoom: { show: false, }, magicType: { show: false, }, }, iconStyle: { normal: { borderColor: '#1990DA', }, }, top: 15, right: 35, }, // 地理坐标系组件 geo: { map: this.parentInfo.length === 1 ? 'china' : 'map', zoom: 1.1, // 当前视角的缩放比例 roam: true, // 鼠标缩放和平移 center: this.parentInfo.length === 1 ? ['118.83531246', '32.0267395887'] : false, tooltip: { trigger: 'item', formatter: p => { let val = p.value[2] if (window.isNaN(val)) { val = 0 } let txtCon = "<div style='text-align:left'>" + p.name + ':<br />销售额:' + val.toFixed(2) + '万</div>' return txtCon }, }, label: { normal: { show: true, color: 'rgb(249, 249, 249)', //省份标签字体颜色 formatter: p => { switch (p.name) { case '内蒙古自治区': p.name = '内蒙古' break case '西藏自治区': p.name = '西藏' break case '新疆维吾尔自治区': p.name = '新疆' break case '宁夏回族自治区': p.name = '宁夏' break case '广西壮族自治区': p.name = '广西' break case '香港特别行政区': p.name = '香港' break case '澳门特别行政区': p.name = '澳门' break default: break } return p.name }, }, emphasis: { show: true, color: '#f75a00', }, }, itemStyle: { normal: { areaColor: '#24CFF4', borderColor: '#53D9FF', borderWidth: 1.3, shadowBlur: 15, shadowColor: 'rgb(58,115,192)', shadowOffsetX: 7, shadowOffsetY: 6, }, emphasis: { areaColor: '#8dd7fc', borderWidth: 1.6, shadowBlur: 25, }, }, }, }, options: [], } this.timeTitle.forEach(item => { var xData = [], yData = [] var min = mapData[item][mapData[item].length - 1].value var max = mapData[item][0].value if (mapData[item].length === 1) { min = 0 } mapData[item].forEach(c => { xData.unshift(c.name) yData.unshift(c.value) }) option.options.push({ backgroundColor: '#012248', title: [ { left: 'center', top: 10, text: item + this.parentInfo[this.parentInfo.length - 1].cityName + '销售额统计图(可点击下钻到县)', textStyle: { color: 'rgb(179, 239, 255)', fontSize: 16, }, }, { text: '销售总额:' + sum[item].toFixed(2) + '万', left: 'center', top: '6.5%', textStyle: { color: '#FFAC50', fontSize: 26, }, }, ], // 视觉映射组件 visualMap: { min: min, max: max, left: '3%', bottom: '5%', calculable: true, // 显示拖拽手柄 seriesIndex: [0], inRange: { color: ['#24CFF4', '#2E98CA', '#1E62AC'], }, textStyle: { color: '#24CFF4', }, }, // 柱状图横轴 xAxis: { type: 'value', scale: true, position: 'top', boundaryGap: false, splitLine: { show: false, }, axisLine: { show: true, lineStyle: { color: '#455B77', }, }, axisTick: { show: false, }, axisLabel: { margin: 2, textStyle: { color: '#c0e6f9', }, }, }, // 柱状图纵轴 yAxis: { type: 'category', nameGap: 16, axisLine: { show: true, lineStyle: { color: '#455B77', }, }, axisTick: { show: false, }, axisLabel: { interval: 0, textStyle: { color: '#c0e6f9', }, }, data: xData, }, series: [ { name: item + '销售额度', type: 'map', geoIndex: 0, map: this.parentInfo.length === 1 ? 'china' : 'map', roam: true, zoom: 1.3, tooltip: { trigger: 'item', formatter: p => { let val = p.value if (p.name == '南海诸岛') return if (window.isNaN(val)) { val = 0 } let txtCon = "<div style='text-align:left'>" + p.name + ':<br />销售额:' + val.toFixed(2) + '万</div>' return txtCon }, }, label: { normal: { show: false, }, emphasis: { show: false, }, }, data: mapData[item], }, { name: '散点', type: 'effectScatter', coordinateSystem: 'geo', rippleEffect: { brushType: 'fill', }, label: { normal: { formatter: p => { return p.value[2].toFixed() }, position: 'center', //地图上是否有文字 show: true, textStyle: { color: '#fff' } }, emphasis: { show: false } }, itemStyle: { normal: { color: '#F4E925', shadowBlur: 10, shadowColor: '#333', }, }, data: pointData[item], symbolSize: function(val) { let value = val[2] if (value == max) { return 27 } return 10 }, showEffectOn: 'render', //加载完毕显示特效 }, { type: 'bar', barGap: '-100%', barCategoryGap: '60%', itemStyle: { normal: { color: '#11AAFE', }, emphasis: { show: false, }, }, data: yData, }, ], }) }) mapChart.setOption(option, true) //点击前解绑,防止点击事件触发多次 mapChart.off('click') mapChart.on('click', this.echartsMapClick) }, //echarts点击事件 echartsMapClick(params) { if (!params.data) { return } else { //如果当前是最后一级,那就直接return if ( this.parentInfo[this.parentInfo.length - 1].code == params.data.cityCode ) { return } let data = params.data this.parentInfo.push({ cityName: data.name, level: data.level, code: data.cityCode, }) this.getGeoJson(data.cityCode) } }, //选择切换市县 chooseArea(val, index) { if (this.parentInfo.length === index + 1) { return } this.parentInfo.splice(index + 1) this.getGeoJson(this.parentInfo[this.parentInfo.length - 1].code) }, }, mounted() { this.getGeoJson(100000) }, } </script> <style lang="scss" scoped> .app-container { #chinaMap { width: 1400px; height: 800px; } .mapChoose { position: absolute; left: 20px; top: 55px; color: #eee; .title { padding: 5px; border-top: 1px solid rgba(147, 235, 248, 0.8); border-bottom: 1px solid rgba(147, 235, 248, 0.8); cursor: pointer; } .icon { font-family: 'simsun'; font-size: 25px; margin: 0 11px; } } } </style>
resize.js
import { debounce } from "@/utils/index.js" export default { data() { return { myChart: null, resizeHandler: null } }, mounted() { this.resizeHandler = debounce(() => { if (this.myChart) { this.myChart.resize() } }, 200) this.initResizeEvent(); }, beforeDestroy() { this.destroyResizeEvent() if (!this.myChart) { return } this.myChart.dispose() this.myChart.off('click') this.myChart = null }, activated() { this.initResizeEvent() }, deactivated() { this.destroyResizeEvent() }, methods: { //监听resize initResizeEvent() { window.addEventListener('resize', this.resizeHandler) }, //移除resize destroyResizeEvent() { window.removeEventListener('resize', this.resizeHandler); } } }
index.js
/** * 函数防抖 * @param {Function} func 包装的函数 * @param {num} delay 延迟时间 * @param {boolean} immediate 第一次滚动会执行两次 开始滚动和结束滚动的时候 * @return {*} */ export function debounce(func, delay, immediate = false) { let timer, context = this return (...args) => { if (immediate) { func.apply(context, args) immediate = false return } clearTimeout(timer) timer = setTimeout(() => { func.apply(context, args) }, delay) } }