【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資源文件下載

相關文章
相關標籤/搜索