【Vue/JS】echarts散点地图,可下钻到省市区级,鼠标悬于散点展现提示框,showLoading/showModal组件封装

功能:javascript

  • Vue/JS中使用echarts实现散点地图
  • 地图点击可进行下钻,最深层级到区级别,双击返回全国地图
  • 鼠标悬于散点时,信息框提示当前散点对应的城市名称
  • 封装了一个相似微信showLoading()/hideLoading()/showModal()的组件

效果图

使用方式:

1. 引入echartMapOptions.js文件
2. new MapDrillDown(dom, this).init() 传入参数便可使用
// js中使用document.getElementById('chart')获取dom对象
// Vue中使用this.$refs.chart获取dom对象
复制代码

echartMapOptions.js内容以下:html

import echarts from 'echarts' // javascript环境下注释掉
import $ from 'jquery' // javascript环境下注释掉
import china from './china.js' // javascript环境下注释掉
import {cityNameData, provinceNameChineseToEng, cityNameChineseToEng} from './geoNameDictionary.js' // javascript环境下注释掉
// geoNameDictionary.js 文件为全部省市区中英文对照json数据,以及中文名和英文名互相转换的方法
复制代码
function MapDrillDown (echartDom, obj) {
  this.chartDom = echarts.init(echartDom) // 参数为地图div的dom对象
  this.optionMap = null // 地图配置信息
  this.tag = 0 // tag: 0全国 1省 2市  标记当前层级
  this.timer = null // 点击事件定时器
  this.provinceOrCityName = '' // 当前省/市名称
  this.lastProvinceOrCityName = ''  // 上一层级的省/市名称
  this.loadingObj = obj.$message // showLoading()/hideLoading()调用对象,   javascript环境下注释掉
}
复制代码

MapDrillDown挂事件vue

MapDrillDown.prototype = {
  // 设置区域颜色
  setRegions: function (regionsJson) {
    var colors = ['#083967', '#13548d', '#1c74b2']
    var colorsLen = colors.length
    var features = regionsJson.features
    var echatsRegions = []
    // var echatsRegions=[{
    //     name: '南海诸岛',
    //     value: 0,
    //     itemStyle: {
    //         normal: {
    //             opacity: 0,
    //             label: {
    //                 show: false
    //             }
    //         }
    //     }
    // }];

    for (var i = 0, len = features.length; i < len; i++) {
      var regionsItem = {
        name: features[i].properties.name,
        itemStyle: {
          normal: {
            areaColor: colors[Math.floor(Math.random() * colorsLen)]
          }
        }
      }
      echatsRegions.push(regionsItem)
    }
    return echatsRegions
  },
}
复制代码

地图样式配置信息java

MapDrillDown.prototype = {
  ...
  setMap: function () {
    this.optionMap = {
      tooltip: {
        trigger: 'item',
        enterable: true, // 鼠标是否能进入提示框内
        formatter: function (params) {
          // 这里配置鼠标悬于散点时,提示框的样式及展现的内容
          var content = ''
          if (params.value !== undefined) {
            content = `<p style='text-align: center;min-width: 100px;'><span class='dpb' style='padding: 5px 8px;font-family: 微软雅黑;font-size: 18px;color: #ffffff;'>${params.name}</span><br></p>`
          }
          return content
        }
      },
      geo: {
        map: 'china',
        label: {
          normal: {
            show: true,
            color: '#639bc3'
          }
        },
        itemStyle: {
          normal: {
            areaColor: '#083967',
            borderColor: '#48c7ff',
            borderWidth: 2
          },
          emphasis: {
            areaColor: '#48c7ff' // 高亮效果
          }
        }
      },
      series: [
        {
          name: '',
          type: 'scatter',
          coordinateSystem: 'geo',
          opacity: 1,
          // 散点数据
          data: convertData(data),
          symbolSize: 10, // 散点图的大小
          label: {
            normal: {
              show: false
            }
          },
          itemStyle: {
            normal: {
              color: '#00d0e4',
              borderColor: '#fff',
              borderWidth: 2
            },
            emphasis: {
              borderColor: '#fff',
              borderWidth: 2
            }
          }
        }
      ]
    }
    // 图表自适应
    window.addEventListener('resize', function () {
      this.chartDom.resize()
    }.bind(this))

    this.optionMap.geo.regions = this.setRegions(china) // 设置区域颜色
    this.chartDom.setOption(this.optionMap)
  },
  
  ...
}
复制代码

注册单击/双击事件jquery

MapDrillDown.prototype = {
  ...
  
  setClick: function () {
    let that = this
    // 点击事件
    that.chartDom.on('click', function (params) { // 点击事件
      clearTimeout(that.timer)
      that.timer = setTimeout(function () {
        if (params.componentType === 'geo') { // 点击地图区域
          that.reFreshMap(params.name)
        }
      }, 300)
    })
    // 这里使用定时器的缘由是:双击是会触发两次单击事件,加时间是双击事件优先被执行,而后在双击事件回调中清除单击事件定时器

    // 双击事件
    that.chartDom.on('dblclick', function (params) {
      clearTimeout(that.timer) // 清除单击事件定时器,使双击时再也不触发单击事件
      that.tag = 0
      that.optionMap.series[0].data = convertData(data)
      that.optionMap.geo.map = 'china'
      that.chartDom.setOption(that.optionMap)
    })
  },
  
  ...
}
复制代码

点击下钻处理函数git

MapDrillDown.prototype = {
  ...
  
  reFreshMap: function (paramsName) {
    let that = this
    this.loadingObj.showLoading({
      title: '正在加载...'
    }) // javascript环境下注释掉
    // 当前处于省级或直辖市级
    if (that.tag === 0) {
      // 获取当前点击的省级名称
      this.provinceOrCityName = paramsName
      let provinceEngName = provinceNameChineseToEng(this.provinceOrCityName)
      // 将省级名称转为英文名称,并获取对应省的地图json资源
      // 这里的json资源能够下载下来放在本地,引用本地路径,也能够是网络资源
      $.get('https://orangleli.github.io/imagesResources/echartMapResources/geoProvince/' + provinceEngName + '.json', function (mapJson) {
        that.tag++
        that.loadingObj.hideLoading() // javascript环境下注释掉
        that.optionMap.series[0].data = convertData(data, provinceEngName)
        that.optionMap.geo.map = provinceEngName
        echarts.registerMap(provinceEngName, mapJson)
        that.chartDom.setOption(that.optionMap)
      })
      this.lastProvinceOrCityName = this.provinceOrCityName
    } else if (that.tag === 1) {
      // 当前处于市级
      // 直辖市只能下钻到区层级
      if (this.lastProvinceOrCityName.includes('直辖市') > 0 || this.lastProvinceOrCityName.includes('台湾省') > 0) {
        that.loadingObj.hideLoading() // javascript环境下注释掉
        return
      }
      this.provinceOrCityName = paramsName
      var provinceEngName = provinceNameChineseToEng(this.lastProvinceOrCityName)
      let cityNameEng = cityNameChineseToEng(that.provinceOrCityName, provinceEngName)
      $.get('https://orangleli.github.io/imagesResources/echartMapResources/city/' + provinceEngName + '/' + cityNameEng + '.json', function (mapJson) {
        that.tag++
        that.loadingObj.hideLoading() // javascript环境下注释掉
        that.optionMap.series[0].data = convertData(data, provinceEngName, cityNameEng)
        that.optionMap.geo.map = provinceEngName
        echarts.registerMap(provinceEngName, mapJson)
        that.chartDom.setOption(that.optionMap)
      })
    } else {
      that.loadingObj.hideLoading() // javascript环境下注释掉
    }
  },
  init () {
    this.setMap()
    this.setClick()
  }
  
  ...
}
复制代码

另:处理散点的方法github

// 当处于全国地图时,展现全部散点,
// provinceEngName 传入省名称时过滤掉全部非当前省的散点
// provinceEngName,cityNameEng 传入省,市名称时过滤掉全部非当前省,当前市的散点
var convertData = function (data, provinceEngName, cityNameEng) {
  var res = []
  for (var i = 0; i < data.length; i++) {
    if (provinceEngName) {
      let ret = cityIsInclude(provinceEngName, data[i].name, cityNameEng)
      if (ret) {
        var geoCoord = geoCoordMap[data[i].name]
        if (geoCoord) {
          res.push({
            name: data[i].name,
            value: geoCoord.concat(data[i].value)
          })
        }
      }
    } else {
      let geoCoord = geoCoordMap[data[i].name]
      if (geoCoord) {
        res.push({
          name: data[i].name,
          value: geoCoord.concat(data[i].value)
        })
      }
    }
  }
  return res
}
// 遍历判断全部散点是否属于provinceEngName传入的省,cityNameEng传入的市
let cityIsInclude = function (provinceEngName, cityName, cityNameEng) {
  let cities = cityNameData[`cityName_${provinceEngName}`]
  for (let city in cities) {
    if ((!cityNameEng && city.indexOf(cityName) !== -1) || (cityNameEng && city.indexOf(cityName) !== -1 && cities[city] === cityNameEng)) {
      return true
    }
  }
  return false
}
复制代码

导出npm

// javascript环境下注释掉
export {
  MapDrillDown
}
复制代码

以上是echartMapOptions.js的源码json

Vue中使用

npm install echarts --save
npm install jquery --save
复制代码

.vue组件中引入

<template>
    <div ref="chart" class="mapCls"></div>
</template>

<script>
import {MapDrillDown} from './js/echartMapOptions.js'
export default {
  name: 'echartMapShow',
  mounted () {
    new MapDrillDown(this.$refs.chart, this).init() // 参数为地图div的dom对象
  }
}
</script>

<style scoped>
  .mapCls{
    width: 100%;
    height: 1000px; 
    background-image: url(./bg.png);
    background-size: 100% 100%;
    background-repeat: no-repeat;
  }
</style>
复制代码
  • 要指定地图大小,不然没法渲染

Vue中使用echarts的源码bash

* npm install
* npm run dev
复制代码

showLoading/showModal组件封装

loading组件:

<div class="container" v-if="isShow">
  <img class="icon" :src="iconImg"/>
  <div class="title">{{title}}</div>
</div>
复制代码
<script>
export default {
  name: 'loading',
  data () {
    return {
      isShow: false,
      loadingImg: 'data:image/gif;base64,R0lGODlhZ...', // loading图片base64串
      iconImg: '',
      title: '加载中...',
      duration: 1500,
      timer: null
    }
  },
  methods: {
    showLoading (obj) {
      let that = this
      that.iconImg = that.loadingImg
      that.isShow = true
      if (obj) {
        that.title = obj.title
      }
      that.duration = -1
    },
    hideLoading () {
      let that = this
      this.isShow = false
      if (that.timer) {
        clearInterval(that.timer)
        that.timer = null
      }
    },
    showToast (obj) {
      let that = this
      that.isShow = true
      if (obj) {
        that.title = obj.title || that.title
        if (obj.icon === '') {
          that.iconImg = that.loadingImg
        } else if (obj.icon === 'success') {
          that.iconImg = that.successImg
        } else if (obj.icon === 'none') {
          that.iconImg = ''
        }
        that.duration = obj.duration || that.duration
      }
      that.delayHide()
    },
    hideToast () {
      this.isShow = false
    },
    delayHide () {
      let that = this
      if (that.duration >= 0) {
        that.timer = window.setInterval(that.hideLoading, that.duration)
      }
    }
  }
}
</script>
复制代码

仿微信的wx.showLoading()、wx.hideLoading()、wx.showToast()、wx.hideToast()方法

showModal组件:

<div class="mask" v-if="isShow">
  <div class="container">
    <div class="contentGroup">
      <div class="title">{{title}}</div> <!--标题-->
      <div class="content">{{content}}</div> <!--内容-->
    </div>
    <div class="buttonGroup lineBorderBefore">
      <div v-if="showCancel" class="cancel lineBorderRight" :style="'color: ' + cancelColor + ';'" @click.prevent.stop="cancelClick">{{cancelText}}</div>  <!--取消按钮题-->
      <div class="confirm" :style="'color: ' + confirmColor + ';'" @click.prevent.stop="confirmClick">{{confirmText}}</div>  <!--肯定按钮-->
    </div>
  </div>
</div>
复制代码
<script>
export default {
  name: 'wxModal',
  data () {
    return {
      isShow: false,
      title: '提示',
      content: '点击确认',
      showCancel: true,
      cancelText: '取消',
      cancelColor: '#000',
      confirmText: '确认',
      confirmColor: '#ff6a0b',
      cancelCallback: null,
      confirmCallback: null
    }
  },
  methods: {
    showModal (obj) {
      let that = this
      that.isShow = true
      if (obj) {
        that.title = obj.title
        that.content = obj.content
        that.showCancel = obj.showCancel === undefined || obj.showCancel === true
        that.cancelText = obj.cancelText || that.cancelText
        that.cancelColor = obj.cancelColor || that.cancelColor
        that.confirmText = obj.confirmText || that.confirmText
        that.confirmColor = obj.confirmColor || that.confirmColor
        if (that.showCancel) {
          that.cancelCallback = obj.cancelClick
        }
        that.confirmCallback = obj.confirmClick
      }
    },
    cancelClick (callback) {
      let that = this
      that.isShow = false
      that.cancelCallback && that.cancelCallback()
    },
    confirmClick (callback) {
      let that = this
      that.isShow = false
      that.confirmCallback && that.confirmCallback()
    }
  }
}
</script>
复制代码

全局注册loading、showModal组件:

import loadingComponent from './loading.vue'
import wxModalComponent from './wxModal.vue'
const loading = {
  install: function (Vue) {
    let LoadingProfile = Vue.extend(loadingComponent)
    let loadingPro = new LoadingProfile()
    let AlertProfile = Vue.extend(wxModalComponent)
    let alertPro = new AlertProfile()
    document.body.appendChild(loadingPro.$mount().$el)
    document.body.appendChild(alertPro.$mount().$el)
    // 将方法挂到Vue原型对象上去,经过this.$message.showLoading() 就可使用
    Vue.prototype.$message = {
      showLoading (obj) {
        return loadingPro.showLoading(obj)
      },
      hideLoading () {
        return loadingPro.hideLoading()
      },
      showToast (obj) {
        return loadingPro.showToast(obj)
      },
      hideToast () {
        return loadingPro.hideToast()
      },
      showModal (obj) {
        return alertPro.showModal(obj)
      }
    }
  }
}
export default loading
复制代码

loading、showModal组件使用方法

1. 将alertModal文件整个赋值到components文件夹下
2. 在main.js中添加:
import alertModal from './components/alertModal/alertModal.js'
Vue.use(alertModal)
3. 在组件中就可使用了,如 this.$message.showLoading(),注意this的指向哦
复制代码

注意this的指向:好比本文MapDrillDown原型对象挂载的方式中使用时,其this并不指向Vue原型对象,因此在组件中new MapDrillDown(this.$refs.chart, this).init()的时候,把this对象当作参数传递过去了

alertModal源码地址

JS中使用

echartMapOptions.js文件稍做修改,不用import,export,因此如下代码要进行注释

// import echarts from 'echarts'
// import $ from 'jquery'
// import china from './china.js'
// import {cityNameData, provinceNameChineseToEng, cityNameChineseToEng} from './geoNameDictionary.js'

// export {
//   MapDrillDown
// }
复制代码

同时loading组件和showModal组件也不能使用

function MapDrillDown (echartDom, obj) {
  this.chartDom = echarts.init(echartDom)
  this.optionMap = null
  // tag: 0全国 1省 2市
  this.tag = 0
  this.timer = null
  this.clickLock = true
  this.provinceOrCityName = ''
  this.lastProvinceOrCityName = ''
  // this.loadingObj = obj.$message  这里要进行注释
}

// this.loadingObj.showLoading({
//   title: '正在加载...'
// })

// that.loadingObj.hideLoading()
复制代码

HTML中引入

<!DOCTYPE html>
<html lang="en">
<head>
	<meta charset="UTF-8">
	<title>地图下钻</title>
	<style>
	.mapCls{
           width: 100%;
           height: 1000px;
           background-image: url(./bg.png);
           background-size: 100% 100%;
           background-repeat: no-repeat;
	}
	</style>
</head>
<script src="./resources/jquery.min.js"></script>
<script src="./resources/echarts.js"></script>
<script src="./resources/china.js"></script>
<script src="./resources/geoNameDictionary.js"></script>
<body>
	<div id="chart" class="mapCls"></div>
</body>
<script src="./echartMapOptions.js"></script>
<script>    
    new MapDrillDown(document.getElementById('chart')).init();
</script>
</html>
复制代码

js中使用echarts的源码

* 运行index.html文件
复制代码

省市级地图json资源文件下载

相关文章
相关标签/搜索