大家好呀,我是一个前端小倒霉蛋wangly。
近期一直忙于业务开发,和复习知识点,就没有更新文章。刚好最近业务上做了一个地图的下钻和原点图。就将它写成一篇文章吧。与诸君共勉。当然,如果最近的项目中需要制作这个功能。也可以直接clone代码。根据自己的需求实现相应功能。
ECharts的地图
json
文件可以在Github
中找到。在项目地址也可以直接复制哦。
这次就以一个省市区下钻
的案例来做一下吧。也是我写在自己的新项目里面的。还没有进行美化。项目是vue开发的,React,JQuery...等可以按照思路参考。全国地图只需要多处理一层配置就好了。文章看下来。我想应该能明白原理
流程图和基本效果(未扩展)
流程图看上去是这样的。当ECharts容器初始化后,对地图进行注册。按照自己需要加载注册不同的组件。随后设置OptionConfig
。这样,地图就会在你定义的DOM容器中展示出来了。
初始化容器
在模板中添加一个id名为map
的div标签作为ECharts
的初始化容器。
<template>
<div class="home">
<a-breadcrumb>
<a-breadcrumb-item
v-for="[key, value] in hashMap" :key="key">
<span @click="readyMap(key, value)">{{value.name}}</span>
</a-breadcrumb-item>
</a-breadcrumb>
<!-- map容器 -->
<div id="map" style="width: 500px;height: 500px;"></div>
</div>
</template>
随后,在mounted
声明周期中初始化map
容器,这里不论是用ref
还是document
按照自己的风格来获取DOM
mounted () {
this.$nextTick(() => {
this.cityMap = echarts.init(document.getElementById('map'))
this.registerMap()
})
}
在初识化后面,跟随了一个registerMap
的方法,主要是用来注册并且绘制地图的。那么来看下它做了什么吧。
绘制地图
registerMap
听起来名称是有点误会的。也是我想的不周到,其实它相当于一个main
,相当于一个方法容器,如下图,将绘制一系列地图的拆分成为一个模块,其实是比较好理解的。而registerMap
的作用就是将这些module methods
给加载进来,可以看到rendHEAD
方法,这个其实是绘制主地图的方法,本文用某省作为HEAD
头层,那么它们的关系看上去就是一个单向链表,一节一节的将下级城市推演出来。那么,一起看看是如何绘制它们的吧?
rendHEAD
绘制省级地图rendCity
绘制市区地图- Huizhi
registerMap () {
console.log('开始注册地图')
// 加载头部地图
this.rendHEAD()
// 点击事件
this.cityMap.on('click', $mod => {
const { name, value } = $mod.data
switch ($mod.data.zoom) {
case 1:
this.rendCity(name, value)
this.setHashMap($mod.data.zoom + 1, name, value)
break
case 2: {
const oneMap = {
type: 'FeatureCollection',
features: [$mod.data.data],
parent: value,
zoom: 3
}
echarts.registerMap(name, oneMap)
this.renderMap(name, [{
name: name,
parent: value,
zoom: 3
}])
this.setHashMap($mod.data.zoom + 1, name, value)
} break
case 3:
this.rendHEAD()
this.clearHashMap()
break
}
})
}
绘制头层(省级)下的城市
JiangxiMap
是我们通过require
引入进来的JSON
,这个可以根据自己喜好来决定是用ajax请求还是通过模块加载都可以。只需要将json
中的文件内容拿到就好了。JSON
中的features
记录了需要的下级市信息,通过一个循环,将城市名称内容都集中到一个数组,它最终将作为ECharts
的data
来显示,可以自定义你需要的数据。为将来点击事件做处理。然后将ECharts.registerMap
来注册地图信息,最后通过renderMap
方法进行地图展示。
rendHEAD () {
const mapName = []
JiangxiMap.features.forEach(city => {
// 城市名称信息
mapName.push({
// 名称
name: city.properties.name,
// 城市编码,对应文件名
value: city.id,
// 层级
zoom: 1
})
})
// 注册
echarts.registerMap('江西省', JiangxiMap)
// 加载
this.renderMap('江西省', mapName)
}
绘市级内容
在绘制省级内容的时候,传入了一个mapName
的配置对象数组。当点击ECharts地图区域时,它会返回一个当前市的对象出来。如图$mod.data
就是传入进行的数据。参考上面,很快就理解了这个数据到底是什么。根据value
去导入或请求当前市的数配置文件,结构和绘制省是一样。一样的流。遍历features
后这次拿到的是当前市下所有的县区资料。这里注意,将遍历出来的整个箱放在countyName[data]
中,让它带入到各自的县中去。为什么这么做下面绘制县区会做解释。照葫芦画瓢一样的。
// 绘制市级别地图
rendCity (name, value) {
// name代表的是该地区中文名称
// value 代表行政编号,如:3600000
const fileName = value
const cityData = require(`@/plugin/map/city/${fileName}.json`)
echarts.registerMap(name, cityData)
// 县区名称配置
const countyName = []
cityData.features.forEach(county => {
countyName.push({
name: county.properties.name,
value: null,
data: county,
parent: value,
zoom: 2
})
})
// 加载地图
this.renderMap(name, countyName)
},
绘制县区内容
因为县区内容的独立性。代表着,县区是没有下级的。因此就不需要配置文件展现下级。上个市区绘制时,在Echarts的
data
中直接放入一个城市数据
。所以可以用来伪造成一个块就可以单独展示了。也就是在市的基础上,抛弃掉非进入县区的地图。
将点击地图县的时候,上一层就注入的data
此时就是当前县区的地图配置。但是因为没有节点,所以它不能作为json
直接使用注册,z而是做了一层蒙版。可以看到oneMap
,相当于给它套了个壳,当等于:
- 市级别地图展现的内容:['县1', '县2', '区1']
- 套模板后显示的内容:['当前县']
通过包装,在通过渲染方法,可以得到县的地图展示。
// 生成单独的县区数据,
const oneMap = {
type: 'FeatureCollection',
features: [$mod.data.data],
value: null,
parent: value,
zoom: 3
}
// 注册
echarts.registerMap(name, oneMap)
// 加载
this.renderMap(name, [{
name: name,
parent: value,
zoom: 3
}])
rendMap加载方法
默认的option
中,是一个固定的内容。主要是绘制功能。而rendMap
中则是对图的表述。例如文本内容,样式,展现的一些东西。传入了mapName
,cityNameList
两个参数。通过传入的内容,a生成不一样的地图片段。这样初步的绘制流程就绘制完成了。下面是要对点击效果做一些功能。
- mapName: 注册地图的显示名称
- cityNameList: 注册地图的数据,给
optionsData
的值
// 运行地图
renderMap(mapName, cityNameList) {
option.series = [{
name: mapName,
// 地图类型
type: 'map',
mapType: mapName,
nameMap: mapName,
label: {
normal: {
show: true,
textStyle: {
color: '#303753',
fontSize: 13
}
},
emphasis: {
show: true,
textStyle: {
color: '#2a8ab3',
fontSize: 16
}
}
},
// 加载值
data: cityNameList,
itemStyle: {
normal: {
areaColor: 'rgb(78, 163, 151)',
borderWidth: 2,
borderColor: 'rgb(34, 195, 170)',
label: {
show: true,
textStyle: {
color: 'rgb(0, 152, 217)',
fontSize: 12
}
}
},
emphasis: {
areaColor: '#0f6471',
label: {
show: true,
textStyle: {
color: '#fff',
fontSize: 15
}
}
}
}
}]
this.cityMap.setOption(option)
}
点击切换城市
还记得在自定义添加的data
数据中。我存放了一个zoom
吗。以下我通过zoom
做了一个点击事件的区分。通过zoom
我用switch
选择了不同层级的点击事件操作。如果是全国那么就从
1->2->3
变为了 1->2->3->4
层次。理论上都是照葫芦画瓢。
this.cityMap.on('click', $mod => {
const {
name,
value
} = $mod.data
switch ($mod.data.zoom) {
case 1:
this.rendCity(name, value)
this.setHashMap($mod.data.zoom + 1, name, value)
break
case 2: {
const oneMap = {
type: 'FeatureCollection',
features: [$mod.data.data],
value: null,
parent: value,
zoom: 3
}
echarts.registerMap(name, oneMap)
this.renderMap(name, [{
name: name,
parent: value,
zoom: 3
}])
this.setHashMap($mod.data.zoom + 1, name, value)
}
break
case 3:
this.rendHEAD()
this.clearHashMap()
break
}
})
堆栈信息
这里除了做顶部的面包屑之外。也是对应了业务功能。我就不过多赘述了。原理很简单。Map
的值不能重复,那么key
为层级。value
为内容。将zoom
层次的信息做一个堆栈。返回时将下面元素清除。当你一层层打开地图的时候,堆栈就是下图这样的。当堆栈中某一个层级抽离时,会将优先比它先出去的元素给剔除。达到一个不可逆的进行时。具体可以看源码中下图两个方法。非常简单。
源码相关地址
Vue源码: 点击前往
map文件: 点击前往
UI组件库求star
elegance-ui项目地址:点击前往
总结
深夜写完这篇文章,突然有些困乏。看着窗外的小雨。想到了自己。不就像空中的雨滴一样无根漂泊吗。可能也只有写点文章,才能给自己平淡的生活增加点乐趣吧。最近复习有点心累。如果想要绘制全国的下钻也时一样的思路,无非就是HEAD
换成了全国地图罢了。多加了一个节点。整体的逻辑是不变的
。
如果本篇文章对你有帮助,不妨点个赞给我升三级吧。