這篇文章會介紹「 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>