手把手教 Vue3.2+Vite+Echarts 5 绘制3D线条效果中国地图
- 简介
- 安装插件
- 1、下载并引入 echarts
- 2、下载地图的 json 数据
- 3、全局引入或局部引入(我这里选择单页面局部引入)
- 4、开始绘制流线中国地图
- 项目实践
- 总结:
简介
本篇文章介绍 Vue3.2+Vite 项目内使用 Echarts 5 绘制中国地图,标记分布点!之前没有接触过 Echarts 的,可以先去官方示例看看,里面图形特别齐全。但是官方文档看着费劲的,太多了根本记不住,所以自己做个总结,下次就可以直接使用了,不用做重复无用功。
安装插件
1、下载并引入 echarts
Echarts 已更新到了 5.0以上 版本,安装完记得检查下自己的版本是否是 5.0以上版本 。
安装完成之后,在 package.json 中检查是否安装成功?
// `安装`
npm install echarts --save
2、下载地图的 json 数据
阿里云下载中国以及各个省份地图数据,免费的文件下载地址:
传送门【下载地址】
引入需要绘制的地图数据 json 或 js 文件,本文使用的是 json 格式
:
// 地图的 json 数据
import chinaJson from '../utils/china.json'
3、全局引入或局部引入(我这里选择单页面局部引入)
1、全局引入在man.js中引入挂载;
2、单页面引入方式:
<script setup>
import * as echarts from 'echarts'
import chinaJson from '../utils/china.json'
</script>
3、准备容器
//
<div ref="chinaMap" class="map"></div>
4、实例化 echarts 对象
<script setup>
import * as echarts from 'echarts'
import chinaJson from '../utils/china.json' // 地图的 json 数据
// 初始化
const chinaMap = ref()
var myChart = echarts.init(chinaMap.value)
echarts.registerMap('china', chinaJson) //注册可用的地图
</script>
5、指定配置项和数据
var option = {
// 存放需要绘制图片类型,以及样式设置
}
4、开始绘制流线中国地图
1、开始绘制
const drawChina = () => {
var myChart = echarts.init(chinaMap.value)
echarts.registerMap('china', chinaJson) //注册可用的地图
var option = {
tooltip: {
trigger: 'item',
backgroundColor: 'rgba(252, 252, 252, 0.82)',
borderColor: '#fff',
showDelay: 0,
hideDelay: 0,
enterable: true,
transitionDuration: 0,
extraCssText: 'z-index:100',
formatter: (params, ticket, callback) => {
//根据业务自己拓展要显示的内容
// console.log(params,'params',)
var res = ''
var initss = 0
var newArry = ''
var name = params.name
// var value = params.value[params.seriesIndex + 1]
if (params.data.ints > 0) {
initss = params.data.ints
}
if (params.data.tit?.length > 0) {
newArry = Array.from(params.data.tit,(item)=> item.title)
}
res = "<div style='max-width:500px;white-space:pre-wrap;'><span>" + name + '</span>旅游:'+ "<div style='color: red;'>" + newArry + "</div></div>"
return res
},
},
geo: {
show: true,
center: [105.194115019531, 35.582111640625],
map: 'china',
roam: drawChinaObj.roam, //是否允许缩放,拖拽
zoom: drawChinaObj.zoom, //初始化大小
//缩放大小限制
scaleLimit: {
min: 0.1, //最小
max: 12, //最大
},
//设置中心点
//center: [95.97, 29.71],
//省份地图添加背景
//regions: regions,
itemStyle: {
// normal: {
areaColor: '#3352c7',
color: '#e91e63',
borderColor: '#5e84fd',
borderWidth: 2,
// },
},
label: {
color: 'rgba(255,255,255,0.5)',
show: false,
},
//高亮状态
emphasis: {
label: {
show: true,
color: '#fff',
},
itemStyle: {
areaColor: '#e91e63',
},
},
z: 10,
},
//配置属性
series: series,
}
myChart.setOption(option)
}
2、线条动画效果关键代码处理:
var series = []
;[['上海', chinaDatas]].forEach((item, i) => {
series.push(
//设置指向箭头信息
{
type: 'lines',
zlevel: 2,
effect: {
show: true,
period: 4, //箭头指向速度,值越小速度越快
trailLength: 0.02, //特效尾迹长度[0,1]值越大,尾迹越长重
symbol: 'arrow', //箭头图标
symbolSize: 8, //图标大小
},
coords:[[1,2],[2,3]],
lineStyle: {
// normal: {
color: '#adffd0',
width: 1, //尾迹线条宽度
opacity: 1, //尾迹线条透明度
curveness: 0.3, //尾迹线条曲直度
// },
},
data: convertData(item[1]),
},
// 发射点位置涟漪等效果
{
type: 'effectScatter',
coordinateSystem: 'geo',
zlevel: 2,
rippleEffect: {
//涟漪特效
period: 4, //动画时间,值越小速度越快
brushType: 'stroke', //波纹绘制方式 stroke, fill
scale: 4, //波纹圆环最大限制,值越大波纹越大
},
itemStyle: {
// normal: {
show: false,
color: '#f8f9f5',
// },
},
label: {
// normal: {
show: true,
position: 'right', //显示位置
offset: [5, 0], //偏移设置
formatter: function (params) {
//圆环显示文字
return params.data.name
},
fontSize: 13,
// },
emphasis: {
show: true,
},
},
symbol: 'circle',
symbolSize: (val) => {
return 5 + val[2] * 5 //圆环大小
},
data: item[1].map((dataItem) => {
return {
name: dataItem[0].name,
tit: dataItem[0].obj,
ints: dataItem[0].ints,
value: chinaGeoCoordMap[dataItem[0].name].concat([dataItem[0].value]),
}
}),
},
//被攻击点
{
type: 'effectScatter',
coordinateSystem: 'geo',
zlevel: 2,
rippleEffect: {
//涟漪相关
period: 2,
brushType: 'stroke',
scale: 5,
},
itemStyle: {
color: '#f00',
},
label: {
show: true,
position: 'right',
// offset:[5, 0],
color: '#0f0',
formatter: '{b}',
textStyle: {
color: '#fff',
fontSize: 12,
},
emphasis: {
show: true,
color: '#f60',
},
},
symbol: 'circle',
symbolSize: 10, //圆圈大小
data: [
{
name: item[0],
value: chinaGeoCoordMap[item[0]].concat([10]),
},
],
},
)
})
项目实践
最后分享完美案例
<template>
<div class="page">
<div class="h1">管理后台大屏可视化界面</div>
<!-- 封装组件全局挂载 -->
<div class="time">
<mm-time />
</div>
<div class="ul">
<span @click="onClickOpen(1)">还原</span>
<span @click="onClickOpen(2)" :class="{'spanBg': !drawChinaObj.roam}">固定</span>
<span @click="onClickOpen(3)">缩放+</span>
<span @click="onClickOpen(4)">缩放-</span>
<span @click="onClickOpen(5)">刷新</span>
</div>
<!-- 组件小提示 -->
<div class="tip">
<TextSoll />
</div>
<div ref="chinaMap" class="map"></div>
</div>
</template>
<script setup>
import * as echarts from 'echarts'
import chinaJson from '../utils/china.json'
import TextSoll from '@/components/textSoll.vue'
const { proxy } = getCurrentInstance()
onMounted(() => {
drawChina()
})
// 事件
const onClickOpen = (e) => {
if (e == 1) {
drawChinaObj.roam = true
drawChinaObj.zoom = 1.5
} else if (e == 2) {
drawChinaObj.roam = false
} else if (e === 3) {
drawChinaObj.zoom = drawChinaObj.zoom + 0.2
} else if (e === 4) {
drawChinaObj.zoom = drawChinaObj.zoom - 0.2
} else {
drawChina()
}
drawChina()
}
// 初始化
const chinaMap = ref()
const drawChinaObj = reactive({
hint: proxy.$utlis.handleTips(),
roam: false,
zoom: 1.5,
})
/**
* 设置投射点
* 中国地理坐标图
*/
const chinaGeoCoordMap = {
上海: [121.46503408447266, 31.221068849156694],
湖南省张家界: [110.54218025390624, 29.122992832430285],
湖南省凤凰古城: [109.10110225, 27.4452938],
浙江省杭州西湖: [120.1941180322266, 30.23469703027832],
重庆市: [109.40093,31.018551],
广西省北海涠洲岛: [109.23477202734375,20.857876355435824],
哈尔滨市: [127.16687354565428,45.45942366122554],
}
const chinaDatas = [
[
{
name: '湖南省张家界',
value: 1,
ints: 10,
obj: [
{title: '张家界(1次)'},
]
},
],
[
{
name: '湖南省凤凰古城',
value: 1,
ints: 10,
obj: [
{title: '凤凰古城(1次)'},
]
},
],
[
{
name: '浙江省杭州西湖',
value: 1,
ints: 10,
obj: [
{title: '杭州西湖(1次)'}
]
},
],
[
{
name: '重庆市',
value: 1,
ints: 10,
obj: [
{title: '巴南区花园(1次)'},
{title: '奉节县白帝城(1次)'},
]
},
],
[
{
name: '广西省北海涠洲岛',
value: 1,
ints: 10,
obj: [
{title: '北海涠洲岛(1次)'}
]
},
],
[
{
name: '哈尔滨市',
value: 1,
ints: 10,
obj: [
{title: '哈尔滨市'}
]
}
]
]
const scatterPos = [121.46503408447266, 31.221068849156694]
// 1
const convertData = (data) => {
var res = []
for (var i = 0; i < data.length; i++) {
var dataItem = data[i]
var fromCoord = chinaGeoCoordMap[dataItem[0].name]
console.log(dataItem,'fromCoord')
var toCoord = scatterPos
if (fromCoord && toCoord) {
res.push([
{
tit: dataItem[0].obj,
coord: fromCoord,
value: dataItem[0].value,
},
{
coord: toCoord,
},
])
}
}
console.log('res', res)
return res
}
// 2
var series = []
;[['上海', chinaDatas]].forEach((item, i) => {
series.push(
// {
// //绘制一个新地图
// type: 'map',
// map: 'china',
// zoom: 1,
// center: [105.194115019531, 35.582111640625],
// z: -1,
// aspectScale: 0.75, //
// itemStyle: {
// // normal: {
// areaColor: '#f00',
// borderColor: '#090438',
// borderWidth: '2',
// shadowColor: '#090438',
// shadowOffsetX: 0,
// shadowOffsetY: 15,
// // },
// },
// },
//设置指向箭头信息
{
type: 'lines',
zlevel: 2,
effect: {
show: true,
period: 4, //箭头指向速度,值越小速度越快
trailLength: 0.02, //特效尾迹长度[0,1]值越大,尾迹越长重
symbol: 'arrow', //箭头图标
symbolSize: 8, //图标大小
},
coords:[[1,2],[2,3]],
lineStyle: {
// normal: {
color: '#adffd0',
width: 1, //尾迹线条宽度
opacity: 1, //尾迹线条透明度
curveness: 0.3, //尾迹线条曲直度
// },
},
data: convertData(item[1]),
},
// 发射点位置涟漪等效果
{
type: 'effectScatter',
coordinateSystem: 'geo',
zlevel: 2,
rippleEffect: {
//涟漪特效
period: 4, //动画时间,值越小速度越快
brushType: 'stroke', //波纹绘制方式 stroke, fill
scale: 4, //波纹圆环最大限制,值越大波纹越大
},
itemStyle: {
// normal: {
show: false,
color: '#f8f9f5',
// },
},
label: {
// normal: {
show: true,
position: 'right', //显示位置
offset: [5, 0], //偏移设置
formatter: function (params) {
//圆环显示文字
return params.data.name
},
fontSize: 13,
// },
emphasis: {
show: true,
},
},
symbol: 'circle',
symbolSize: (val) => {
return 5 + val[2] * 5 //圆环大小
},
data: item[1].map((dataItem) => {
return {
name: dataItem[0].name,
tit: dataItem[0].obj,
ints: dataItem[0].ints,
value: chinaGeoCoordMap[dataItem[0].name].concat([dataItem[0].value]),
}
}),
},
//被攻击点
{
type: 'effectScatter',
coordinateSystem: 'geo',
zlevel: 2,
rippleEffect: {
//涟漪相关
period: 2,
brushType: 'stroke',
scale: 5,
},
itemStyle: {
color: '#f00',
},
label: {
show: true,
position: 'right',
// offset:[5, 0],
color: '#0f0',
formatter: '{b}',
textStyle: {
color: '#fff',
fontSize: 12,
},
emphasis: {
show: true,
color: '#f60',
},
},
symbol: 'circle',
symbolSize: 10, //圆圈大小
data: [
{
name: item[0],
value: chinaGeoCoordMap[item[0]].concat([10]),
},
],
},
)
})
// 3
const drawChina = () => {
var myChart = echarts.init(chinaMap.value)
echarts.registerMap('china', chinaJson) //注册可用的地图
var option = {
tooltip: {
trigger: 'item',
backgroundColor: 'rgba(252, 252, 252, 0.82)',
borderColor: '#fff',
showDelay: 0,
hideDelay: 0,
enterable: true,
transitionDuration: 0,
extraCssText: 'z-index:100',
formatter: (params, ticket, callback) => {
//根据业务自己拓展要显示的内容
// console.log(params,'params',)
var res = ''
var initss = 0
var newArry = ''
var name = params.name
// var value = params.value[params.seriesIndex + 1]
if (params.data.ints > 0) {
initss = params.data.ints
}
if (params.data.tit?.length > 0) {
newArry = Array.from(params.data.tit,(item)=> item.title)
}
res = "<div style='max-width:500px;white-space:pre-wrap;'><span>" + name + '</span>旅游:'+ "<div style='color: red;'>" + newArry + "</div></div>"
return res
},
},
geo: {
show: true,
center: [105.194115019531, 35.582111640625],
map: 'china',
roam: drawChinaObj.roam, //是否允许缩放,拖拽
zoom: drawChinaObj.zoom, //初始化大小
//缩放大小限制
scaleLimit: {
min: 0.1, //最小
max: 12, //最大
},
//设置中心点
//center: [95.97, 29.71],
//省份地图添加背景
//regions: regions,
itemStyle: {
// normal: {
areaColor: '#3352c7',
color: '#e91e63',
borderColor: '#5e84fd',
borderWidth: 2,
// },
},
label: {
color: 'rgba(255,255,255,0.5)',
show: false,
},
//高亮状态
emphasis: {
label: {
show: true,
color: '#fff',
},
itemStyle: {
areaColor: '#e91e63',
},
},
z: 10,
},
//配置属性
series: series,
}
myChart.setOption(option)
}
</script>
<style lang="less" scoped>
.page {
width: 100%;
height: 100%;
background-image: url(xxxxxxxxxxx);
background-position: 0% 0%;
background-size: 100% 100%;
background-repeat: initial;
background-attachment: initial;
background-origin: initial;
background-clip: initial;
position: relative;
.h1 {
position: absolute;
width: 100%;
top: 4%;
text-align: center;
// padding: 27px 0;
font-size: 26px;
font-weight: bold;
color: #fff;
letter-spacing: 4px;
}
.time {
position: absolute;
width: 300px;
top: 6%;
left: 2%;
color: #fff;
text-align: center;
z-index: 100;
}
.ul {
position: absolute;
width: 100%;
top: 12%;
z-index: 9;
font-size: 12px;
color: #fff;
text-align: center;
margin-top: 10px;
span {
color: #03A9F4;
padding: 4px 12px;
border-radius: 4px;
border: 2px solid #03A9F4;
margin-left: 10px;
cursor: pointer;
&:first-child {
margin-left: 0px;
}
&:active {
color: #fff;
background-color: rgba(137, 192, 243, 0.82)
}
}
.spanBg {
color: #fff;
background-color: rgba(137, 192, 243, 0.82)
}
}
.tip {
position: absolute;
width: 360px;
top: 6%;
right: 2%;
color: #b9b5b5;
float: right;
}
.map {
width: 100%;
margin: auto;
height: calc(100vh);
position: relative;
// width: 100%;
// height: calc(100vh - 90px);
// background: rgba(6, 58, 214, 0.1);
transform: rotate3d(1, 0, 0, 35deg);
}
}
</style>
总结:
前端路上 | 所知甚少,唯善学。
各位小伙伴有什么疑问,欢迎留言探讨。
— 关注我:前端路上不迷路 —