ECharts + geoJSON 繪制地圖_下鉆功能_Vue React 項目組件封裝

這篇文章會介紹「 H5 + 原生 JS」和「 封裝 React 組件」中兩種實現(xiàn)方式。

「 H5 + 原生 JS」

參考文章(1)中的代碼已經(jīng)實現(xiàn)了展示全國地圖和點擊省市自治區(qū)下鉆的功能诺擅,但是每個區(qū)域的顏色是一樣的涂佃。于是又結(jié)合了 參考文章(2)。效果圖:

---- 源碼地址

參考文章:
HTML5 Canvas實現(xiàn)中國地圖 可展開地級市子地圖
echarts實現(xiàn)中國地圖數(shù)據(jù)展示

繪制 geoJSON 地址: http://geojson.io/

封裝 React 組件

繪制地圖調(diào)用的 echarts.registerMap(geoJSON) 這一方法,geoJSON 的具體實現(xiàn)請移步至 echarts搞定各種地圖(想怎么畫就怎么畫)嗡靡。

1. 封裝 geoJSON 數(shù)據(jù)裳朋,結(jié)構(gòu)大致如下:

{
  "anhui": {
    "type": "FeatureCollection",
    "features": [
      {
        "id": "340100",
        "type": "Feature",
        "geometry": {
          "type": "Polygon",
          "coordinates": [
            "[NGHEDAHAPCFMPGTCFULUNGFELKJGDG@ECKMIGIBE@....."
          ],
          "encodeOffsets": [[119842, 32007]]
        },
        "properties": {
          "cp": [117.283042, 31.86119],
          "name": "合肥市",
          "childNum": 1
        }
      },
      {"id": ........},
    ]
}

2. 創(chuàng)建引入 geoJSON 文件的中間文件:

import {anhui} from './province/anhui';
import {aomen} from './province/aomen';
import {beijing} from './province/beijing';
// etc. 

export default [
  {
    anhui,
    aomen,
    beijing,
   // etc.
  },
];

3. 封裝 Map 文件

import React, {Component} from 'react';
import echarts from 'echarts';
import styles from './Map.less';
import * as mapdata from './ProvinceData';
import {china} from './MapData/China';
import * as province from './MapData/province.js';

class CMap extends Component {
  componentDidMount() {
    this.setState({
      province,
    });
    china(echarts);
    this.initEcharts('china', '中國');
  }
  // 初始化echarts
  initEcharts(pName, Chinese_) {
    var myChart = echarts.init(document.getElementById('china-map'));
    var oBack = document.getElementById('back');
    var option = {
      title: {
        text: Chinese_ || pName,
        left: 'center',
      },
      tooltip: {
        trigger: 'item',
        formatter: '病线<br/>{c}',
      },
      //左側(cè)小導航圖標
      visualMap: {
        show: true,
        x: 'left',
        y: 'top',
        splitList: this.props.splitList,
        color: ['#3D74CC', '#5A8EE0', '#6FA4F7', '#80B1FF', '#99C0FF', '#B3D0FF'],
      },

      series: [
        {
          name: Chinese_ || pName,
          type: 'map',
          mapType: pName,
          roam: false, //是否開啟鼠標縮放和平移漫游
          data: this.props.mapData,
          top: '3%', //組件距離容器的距離
          zoom: 1.1,
          selectedMode: 'single',

          label: {
            normal: {
              show: true, //顯示省份標簽
              textStyle: {color: '#585858', fontSize: 12}, //省份標簽字體顏色
            },
            emphasis: {
              //對應的鼠標懸浮效果
              show: true,
              textStyle: {color: '#aaa'},
            },
          },
          itemStyle: {
            normal: {
              borderWidth: 0.5, //區(qū)域邊框?qū)挾?              borderColor: '#A6E1FF', //區(qū)域邊框顏色
              areaColor: '#fff', //區(qū)域顏色
            },

            emphasis: {
              borderWidth: 0.5,
              borderColor: '#FFD1A3',
              areaColor: '#FFDAA6',
            },
          },
        },
      ],
    };

    myChart.setOption(option);

    myChart.off('click');
    let _this = this;
    if (pName === 'china') {
      // 全國時,添加click 進入省級
      myChart.on('click', function(param) {
        for (var i = 0; i < mapdata.provincesText.length; i++) {
          if (param.name === mapdata.provincesText[i]) {
            //顯示對應省份的方法
            const pName = mapdata.provinces[i];
            echarts.registerMap(mapdata.provincesText[i], _this.state.province.default[0][pName]);
            _this.initEcharts(mapdata.provincesText[i]);
            break;
          }
        }
        if (param.componentType === 'series') {
          var provinceName = param.name;
        }
      });
    } else {
      // 省份鲤嫡,添加雙擊 回退到全國
      myChart.on('dblclick', function() {
        _this.initEcharts('china', '中國');
      });
    }
  }

  render() {
    return (
      <div>
        <button
          onClick={() => {
            this.initEcharts('china', '中國');
          }}
        >
          返回全國
        </button>
        <div
          style={{marginTop: '20px', width: '100%', height: document.body.clientHeight * 0.7}}
          id="china-map"
        />
      </div>
    );
  }
}

export default CMap;

4. ProvinceData 數(shù)據(jù):

export var provinces = [
  "shanghai",
  "hebei",
 // etc.
];

export var provincesText = [
  "上海",
  "河北",
  "山西",
// etc.
];

export var seriesData = [
  { name: "北京", value: "100" },
  { name: "天津", value: randomData() },
  { name: "上海", value: randomData() },
  { name: "重慶", value: randomData() },
// 其他城市數(shù)據(jù).....
}];

function randomData() {
  return Math.round(Math.random() * 500);
}

5. 引用組件

import CMap from './Map.js';

<CMap mapData={mapData} />;

2020 更新升級版:

Vue 組件封裝

其實 React 封裝也是同理的送挑,之前的版本是簡易封裝,看著就 low泛范,這一版更新了比較多:

1. 準備 GEOJSON 數(shù)據(jù)

地圖的 JSON 數(shù)據(jù)可以在這里下載
json-data/map

2. 準備 seriesData 數(shù)據(jù)

export const seriesData = [
  { name: '北京', value: '100' },
// ...
]

export const provincesdata = [
  { name: '朝陽市', value: randomData() },
//...
}

function randomData() {
  return Math.round(Math.random() * 500)
}

完整代碼請參考:json-data/map/emap.js

3. 封裝組件

// component/Map.vue

<template>
  <div class="container">
    <div ref="Map" :style="{ height: height, width: width }"></div>
    <el-button
      v-show="ifShowButton"
      class="primary-button"
      type="primary"
      @click="backToWholeNation"
      >返回全國</el-button
    >
  </div>
</template>

<script>
import echarts from 'echarts'
import { mapPath } from '@/static/js/util'
import { getTheme } from '@/static/js/theme'

export default {
  components: {},
// 定義可以接收的屬性
  props: {
    lazyResize: {
      type: Number,
      default: 200
    },
    width: {
      type: String,
      default: '80vw'
    },
    height: {
      type: String,
      default: '80vh'
    },
  // map 的 option 都是由此 config 轉(zhuǎn)換
    config: {
      type: Object,
      required: true
    }
  },

  data() {
    return {
      chart: null,
      option: null,
      mapInfo: {},
      mapKey: '中國',
      baseOption: {},
      ifShowButton: false
    }
  },

  mounted() {
    this.restoreChart()
  },

  beforeDestroy() {
    if (this.chart instanceof Object) {
      this.chart.clear()
      this.chart.dispose()
    }
    this.chart = null
    this.baseOption = null
  },

  methods: {
// 返回全國
    backToWholeNation() {
      this.mapKey = '中國'
      this.ifShowButton = false
      this.convertMapOption(this.$props.config, this.mapKey)
      this.getGeoJson().then(() => this.refreshChart())
    },
// 返回默認的 option 配置項
    getBaseOption() {
      return {
        textStyle: {},
        title: {},
        tooltip: {},
        legend: { show: false },
        dataset: [],
        series: []
      }
    },

    // 根據(jù) props 的 config 配置 option
    convertMapOption(config, mapKey) {
      this.baseOption = this.getBaseOption()
      const { seriesData, provincesdata } = config

      // 地圖標題
      this.baseOption.title = {
        top: 'top',
        left: 'center',
        text: mapKey,
        textStyle: {
          color: '#f3f3f3'
        }
      }

      // 視覺映射組件配置
      this.baseOption.visualMap = {
        type: 'continuous',
        top: 'center',
        left: 'left',
        calculable: true,
        color: [
          '#3b4994',
          '#8c62aa',
          '#a5add3',
          '#be64ac',
          '#5ac8c8',
          '#ace4e4'
        ]
      }

      // 提示框
      this.baseOption.tooltip = {
        trigger: 'item',
        formatter: '让虐<br/>{c}'
      }

      // 數(shù)據(jù)
      this.baseOption.series = [
        {
          name: mapKey,
          type: 'map',
          map: mapKey,
          roam: true,
          data: mapKey === '中國' ? seriesData : provincesdata,
          left: mapKey === '海南' ? '80%' : 'center',
          top: mapKey === '海南' ? '215%' : 'center',
          zoom: mapKey === '海南' ? 6 : 1.1,
          scaleLimit: {
            min: 0.5,
            max: 20
          },
          showLegendSymbol: false,
          label: {
            show: true
          },
          emphasis: {
            label: {
              color: '#545454'
            },
            itemStyle: {
              areaColor: null
            }
          },
          nameMap: {
            中華人民共和國: '中國'
          }
        }
      ]
    },

    // 初始化圖表
    initChart() {
      this.chart = echarts.init(this.$refs.Map, getTheme(this.$props.config))
     
      this.chart.on('click', (params) => {
        // 這里做了限制,僅可下鉆一級罢荡,如果有地級市的下屬區(qū)域數(shù)據(jù)赡突,可以改造這個方法
        if (this.mapKey === '中國') {
          this.mapKey = params.name
          this.ifShowButton = true
          this.convertMapOption(this.$props.config, this.mapKey)
          this.getGeoJson().then(() => this.refreshChart())
        }
      })
    },

    // 繪制圖表
    refreshChart() {
      if (!this.chart) return false
      this.chart.setOption(this.baseOption || {}, true)
      // 添加根據(jù)視口縮放重繪功能
      if (this.lazyResize) {
        window.onresize = () => {
          setTimeout(() => {
            this.chart.resize()
          }, this.lazyResize)
        }
      }
    },

    // 根據(jù) mapKey 獲取 JSON 數(shù)據(jù)
    getGeoJson() {
    // 這個方法封裝在 一個 util.js 文件,可以參考接下來的一段代碼
      const mapInfo = mapPath[this.mapKey]
      return new Promise((resolve, reject) => {
        if (mapInfo instanceof Object) {
          if (mapInfo.registered) {
            resolve(this.mapKey)
            return this.mapKey
          }
        } else {
          return false
        }

        if (mapInfo instanceof Object && mapInfo.key) {
          import(`@/static/json-data/map/${mapInfo.filePath}.json`)
            .then((res) => {
              echarts.registerMap(this.mapKey, res)
              mapInfo.registered = true
              resolve(this.mapKey)
              return this.mapKey
            })
            .catch((error) => {
              throw error
            })
        } else {
          reject(false)
          return false
        }
      })
    },

    async restoreChart() {
      this.convertMapOption(this.$props.config, this.mapKey)
      await this.getGeoJson()
      this.initChart()
      this.refreshChart()
    }
  }
}
</script>
<style lang="less" scoped>
.container {
  height: 80vh;
  margin: 10vh 2vw;
  position: relative;
  .primary-button {
    position: absolute;
    top: 0;
    left: 5vw;
  }
}
</style>

獲取 mapPath 的完整代碼請參考 js/util.js

// util.js
export const mapPath = {
  中國: {
    key: 'china',
    name: '中國',
    filePath: 'china',
    registered: false
  },
//...
}

4. 引用組件

// pages/Map.vue
<template>
    <Map :config="config" :lazy-resize="lazyResize" />
</template>

<script>
import { seriesData, provincesdata } from '@/static/json-data/map/emap.js'
import Map from '@/components/Map.vue'

export default {
  components: {
    Map
  },

  data() {
    return {
      config: { seriesData, provincesdata, theme: 'default', mapKey: '中國' },
      lazyResize: 200,
    }
  },
}
</script>
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末区赵,一起剝皮案震驚了整個濱河市惭缰,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌笼才,老刑警劉巖漱受,帶你破解...
    沈念sama閱讀 217,542評論 6 504
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異骡送,居然都是意外死亡昂羡,警方通過查閱死者的電腦和手機,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,822評論 3 394
  • 文/潘曉璐 我一進店門摔踱,熙熙樓的掌柜王于貴愁眉苦臉地迎上來虐先,“玉大人,你說我怎么就攤上這事派敷∮寂” “怎么了?”我有些...
    開封第一講書人閱讀 163,912評論 0 354
  • 文/不壞的土叔 我叫張陵篮愉,是天一觀的道長腐芍。 經(jīng)常有香客問我,道長试躏,這世上最難降的妖魔是什么猪勇? 我笑而不...
    開封第一講書人閱讀 58,449評論 1 293
  • 正文 為了忘掉前任,我火速辦了婚禮颠蕴,結(jié)果婚禮上埠对,老公的妹妹穿的比我還像新娘络断。我一直安慰自己,他們只是感情好项玛,可當我...
    茶點故事閱讀 67,500評論 6 392
  • 文/花漫 我一把揭開白布貌笨。 她就那樣靜靜地躺著,像睡著了一般襟沮。 火紅的嫁衣襯著肌膚如雪锥惋。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 51,370評論 1 302
  • 那天开伏,我揣著相機與錄音膀跌,去河邊找鬼。 笑死固灵,一個胖子當著我的面吹牛捅伤,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播巫玻,決...
    沈念sama閱讀 40,193評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼丛忆,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了仍秤?” 一聲冷哼從身側(cè)響起熄诡,我...
    開封第一講書人閱讀 39,074評論 0 276
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎诗力,沒想到半個月后凰浮,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 45,505評論 1 314
  • 正文 獨居荒郊野嶺守林人離奇死亡苇本,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 37,722評論 3 335
  • 正文 我和宋清朗相戀三年袜茧,在試婚紗的時候發(fā)現(xiàn)自己被綠了。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片瓣窄。...
    茶點故事閱讀 39,841評論 1 348
  • 序言:一個原本活蹦亂跳的男人離奇死亡惫周,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出康栈,到底是詐尸還是另有隱情,我是刑警寧澤喷橙,帶...
    沈念sama閱讀 35,569評論 5 345
  • 正文 年R本政府宣布啥么,位于F島的核電站,受9級特大地震影響贰逾,放射性物質(zhì)發(fā)生泄漏悬荣。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點故事閱讀 41,168評論 3 328
  • 文/蒙蒙 一疙剑、第九天 我趴在偏房一處隱蔽的房頂上張望氯迂。 院中可真熱鬧践叠,春花似錦、人聲如沸嚼蚀。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,783評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽轿曙。三九已至弄捕,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間导帝,已是汗流浹背守谓。 一陣腳步聲響...
    開封第一講書人閱讀 32,918評論 1 269
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留您单,地道東北人斋荞。 一個月前我還...
    沈念sama閱讀 47,962評論 2 370
  • 正文 我出身青樓,卻偏偏與公主長得像虐秦,于是被迫代替她去往敵國和親平酿。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 44,781評論 2 354

推薦閱讀更多精彩內(nèi)容

  • echarts提供了全國各省市及區(qū)縣的js和json文件羡疗,但是并沒有細化到區(qū)域內(nèi)各街道范圍染服,有時候項目中又有這種需...
    前端很忙閱讀 157,174評論 40 291
  • 如何清除浮動,有哪幾種方法叨恨,區(qū)別是什么 1柳刮、移動端你遇到過什么兼容問題? 1、如果在input設置邊框顏色在ios...
    崽崽不哭閱讀 789評論 0 1
  • 有多少人因為自己的閱讀速度不夠理想痒钝,忍受著龜速的折磨秉颗? 又有多少人為了提高自己的速度,愿意放棄熟悉的閱讀方式送矩,去追...
    梁寶滄閱讀 246評論 0 1
  • 堵車 今天中考蚕甥,玉峰路上車真多,一輛挨看一輛栋荸。 整個大街上六條車道被堵得滿滿的菇怀。汽車...
    耕耘人生閱讀 334評論 2 7
  • 這是王倩的第七次相親,她心里是記得這個數(shù)的晌块,一張張臉又在她的眼前浮現(xiàn)爱沟,她的臉頰早在第三次的時候就不會發(fā)燙了。出...
    園林鳥閱讀 468評論 1 0