手把手教 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>
复制
总结:
前端路上 | 所知甚少,唯善学。
各位小伙伴有什么疑问,欢迎留言探讨。
— 关注我:前端路上不迷路 —