本文将详细解析一个基于 Vue 和 ECharts 的 3D 地图可视化项目,展示如何将地理数据转化为精美的交互式地图。

项目概述

这个项目实现了一个中国地图可视化,具有以下特点:

  • 多层叠加的 3D 地图效果

  • 从深圳到其他城市的动态飞线

  • 交互式城市标记点

  • 精美的视觉效果和动画

核心代码解析

1. 数据准备

首先,我们处理原始数据,提取需要的字段并格式化:

const processedData = originalData.map((item, index) => ({
  id: index,
  city: item.city,
  provinceCapitalX: item.provinceCapitalX,
  provinceCapitalY: item.provinceCapitalY,
  num: 200
}));

2. 地图初始化

在 Vue 组件的 mounted 钩子中初始化 ECharts:

mounted() {
  this.setChartInit();
},
methods: {
  setChartInit() {
    const dom = this.$refs.chartRef;
    this.myChart = echarts.init(dom);
    echarts.registerMap('chinaJson', chinaJson);
    echarts.registerMap('chinaItem', chinaItem);
    
    // 数据处理和选项配置...
  }
}

3. 多层地图设计

这个地图由 5 层叠加组成,每层有不同的视觉效果:

  1. 透视层:最上层,提供边框效果

  2. 实际层:交互层,显示标签和可点击区域

  3. 中国边框层:增强国家轮廓

  4. 中间颜色层:增加深度感

  5. 底层:最暗的背景层

geo: [
  {
    type: 'map',
    map: 'chinaItem',
    zlevel: 3,
    // 透视层配置...
  },
  {
    type: 'map',
    map: 'chinaItem',
    zlevel: 2,
    // 实际层配置...
  },
  // 其他层...
]

4. 飞线和标记点

创建从深圳到各城市的飞线和标记点:

const linesData = this.cityList.map(city => ({
  fromName: FROMCITY,
  toName: city.city,
  coords: [FROMCITYCOORD, [city.provinceCapitalX, city.provinceCapitalY]],
  value: city
}));

const scatterData = this.cityList.map(city => ({
  value: [
    city.provinceCapitalX,
    city.provinceCapitalY,
    city
  ],
  name: city.city.replace('市', '')
}));

5. 交互效果

添加了三种交互事件:

this.myChart.on('click', this.onChartClick);
this.myChart.on('mouseover', this.onChartMouseover);
this.myChart.on('mouseout', this.onChartMouseout);

技术要点

  1. 3D 地图效果:通过多层叠加和不同的 zlevel 值创建 3D 视觉效果

  2. 性能优化:使用 silent 属性减少不必要的交互层计算

  3. 视觉层次:通过阴影、边框和透明度调节创建深度感

  4. 交互设计:精心设计的高亮效果和动画增强用户体验

完整实现建议

  1. 确保已正确导入 ECharts 和 ECharts-GL

  2. 准备高质量的中国地图 JSON 数据

  3. 根据实际需求调整视觉效果参数

  4. 添加响应式设计,适应不同屏幕尺寸

整体代码

<template>
    <div class="chartRef" ref="chartRef" style="height: 100%;width: 100%" />
</template>
<script>

import * as echarts from 'echarts'
import '@/utils/chinaMap'
import "echarts-gl"; //3D地图插件
import chinaJson from '@/utils/chinaOutline.json'

const FROMCITY = [113.28064, 23.125177]
const FROMCITYCOORD = [113.28064, 23.125177]

export default {
  name: 'Demo',
  
  data() {
    return {
     
      cityList:[
  {
    "id": 0,
    "city": "绍兴市",
    "provinceCapitalX": "120.15358",
    "provinceCapitalY": "30.287458",
    "num": 200
  },
  {
    "id": 1,
    "city": "北京市",
    "provinceCapitalX": "116.405289",
    "provinceCapitalY": "39.904987",
    "num": 200
  },
  {
    "id": 2,
    "city": "嘉兴市",
    "provinceCapitalX": "120.15358",
    "provinceCapitalY": "30.287458",
    "num": 200
  },
  {
    "id": 3,
    "city": "锦州市",
    "provinceCapitalX": "118.76741",
    "provinceCapitalY": "41.796768",
    "num": 200
  },
  {
    "id": 4,
    "city": "临沂市",
    "provinceCapitalX": "120.15358",
    "provinceCapitalY": "30.287458",
    "num": 200
  },
  {
    "id": 5,
    "city": "深圳市",
    "provinceCapitalX": "113.28064",
    "provinceCapitalY": "23.125177",
    "num": 200
  }
],
  
    }
  },
  mounted() {
    this.setChartInit()
   
  },
  watch: {
   
  },
  methods: {
   onChartClick(){},
   onChartMouseover(){},
   onChartMouseout(){},
   setChartInit() {
      const dom = this.$refs.chartRef;
      this.myChart = echarts.init(dom);
      echarts.registerMap('chinaJson', chinaJson); // 确保chinaJson是有效的3D地图数据
      echarts.registerMap('chinaItem', chinaItem); // 确保chinaJson是有效的3D地图数据

      // 预处理数据
      const linesData = this.cityList.map(city => ({
        fromName: FROMCITY,
        toName: city.city,
        coords: [FROMCITYCOORD, [city.provinceCapitalX, city.provinceCapitalY]],
        value: city
      }));
      const scatterData = this.cityList.map(city => ({
        value: [
          city.provinceCapitalX,
          city.provinceCapitalY,
          city
        ],
        name: city.city.replace('市', '')
      }));

      const option = {
        backgroundColor: 'transparent',
        geo: [
          // 第一层: 透视层
          {
            type: 'map',
            map: 'chinaItem',
            zlevel: 3,
            zoom: 1,
            aspectScale: 0.86,
            layoutCenter: ['50%', '52%'],
            layoutSize: '100%',
            roam: false,
            silent: true,
            label: {
              show: false,
            },
            itemStyle: {
              borderColor: 'rgba(223, 245, 255, 0.5)',
              shadowColor: 'transparent',
              areaColor: 'transparent',
              borderWidth: 2,
              shadowOffsetY: 0,
              shadowBlur: 25,
              opacity: 1,

            },

          },
          // 第二层: 实际层
          {
            type: 'map',
            map: 'chinaItem',
            zlevel: 2,
            zoom: 1,
            aspectScale: 0.86,
            layoutCenter: ['50%', '52%'],
            layoutSize: '100%',
            roam: false,
            silent: false,
            label: {
              show: true,
              color: 'RGBA(255, 255, 255, 1)',
              fontSize: 16,
              fontWidth: 600
            },
            itemStyle: {
                borderColor: 'rgba(37, 182, 144, 0.5)',
                areaColor: 'transparent',
                borderWidth: 2,
            },
            emphasis: { // 添加高亮样式
              label: {
                show: true,
                color: "RGBA(255,255,255,1)" // 设置为白色或其他你想要的颜色
              },
              itemStyle: {
                areaColor: 'RGBA(14, 53, 42, 0.5)', // 高亮颜色
                borderWidth: 1,
                borderColor: 'rgba(223, 245, 255, 0.2)',
                shadowBlur: 10,
                borderColor: 'rgba(223, 245, 255, 0.2)',
              }
            },
            // 高亮省份
            regions: [{
              name: '广东',
              itemStyle: {
                areaColor: "RGBA(14, 53, 42, 0.4)", // 可选:同时设置区域颜色
                borderWidth: 2,
                borderColor: 'RGBA(255,255,255,1)',
                shadowBlur: 30,
                shadowColor: 'RGBA(255,255,255,1)',
                opacity: 1,
              },
            },
            ],

          },
          // 第三层:中国边框
          {
            type: 'map',
            map: 'chinaJson',
            roam: false,
            zoom: 1,
            silent: true,
            zlevel: -1,
            aspectScale: 0.86,
            layoutCenter: ['50%', '52%'],
            layoutSize: '100%',
            itemStyle: {
                borderColor:'rgba(223, 245, 255, 1)',
                shadowColor: 'RGBA(50, 183, 146, 1)',
                areaColor: 'rgba(4, 162, 122, 0.2)',
                borderWidth: 5,
                shadowOffsetY: 2,
                shadowBlur: 50,

            },
          },
          // 第四层:加深颜色中间层
          {
            type: 'map',
            map: 'chinaItem',
            zlevel: -2,
            zoom: 1,
            aspectScale: 0.86,
            layoutCenter: ['50%', '54%'],
            layoutSize: '100%',
            roam: false,
            silent: true,
            itemStyle: {
                areaColor: 'rgba(4, 162, 122, 0.8)',
                borderColor: 'rgba(223, 245, 255, 0.5)',
                shadowOffsetY: 0,
                shadowColor: 'RGBA(37, 182, 144, 1)',
                borderWidth: 1,

            },
            regions: [
              {
                name: "南海诸岛",
                itemStyle: {
                  borderColor: 'RGBA(37, 182, 144, 1)',
                  shadowColor: 'RGBA(37, 182, 144, 1)',
                  areaColor: 'RGBA(27, 96, 78, 1)',
                  borderWidth: 1,
                  normal: {
                    opacity: 0,
                    label: {
                      show: false,
                      color: "RGBA(255,255,255,1)",
                      fontSize: 12,
                    },
                  },
                },
              },
            ],
          },
          // 第五层:底层
          {
            type: 'map',
            map: 'chinaItem',
            zlevel: -3,
            zoom: 1,
            aspectScale: 0.86,
            layoutCenter: ['50%', '57%'],
            layoutSize: '100%',
            roam: false,
            silent: true,
            itemStyle: {
               borderWidth: 5,
              borderColor: 'RGBA(30, 142, 113, 0.8)',
              shadowColor: 'RGBA(28, 91, 74, 0.8)',
              shadowOffsetY: 0,
              shadowBlur: 10,
              areaColor: 'rgba(5,21,35,0.1)',

            },
            regions: [
              {
                name: "南海诸岛",
                itemStyle: {
                  areaColor: "rgba(0, 10, 52, 1)",
                  borderColor: "rgba(0, 10, 52, 1)",
                  normal: {
                    opacity: 0,
                    label: {
                      show: false,
                      color: "RGBA(255,255,255,1)",
                      fontSize: 12,
                    },
                  },

                },
              },
            ],
          },
        ],


        series: [
          // 主要线条
          {
            type: 'lines',
            coordinateSystem: 'geo',
            zlevel: 10,
            symbol: ['none', 'arrow'],
            symbolSize: 10,
            effect: {
              color: 'rgba(70, 219, 242, 1)',
              show: true,
              period: 6,
              trailLength: 0,
              symbolSize: 5
            },

            lineStyle: {
              normal: {
                color: 'rgba(255, 255, 255, 0.9)',
                width: 1,
                curveness: 0.2
              }
            },
            data: linesData
          },
          // 终点标记
          {
            type: 'effectScatter',
            coordinateSystem: 'geo',
            zlevel: 11,
            silent: false,
            select: {
              disabled: false
            },
            rippleEffect: {
              period: 4,
              brushType: 'stroke',
              scale: 4
            },

            symbolSize: 10,
            itemStyle: {
              normal: {
                color: 'RGBA(255, 166, 1, 1)'
              },
              emphasis: {
                show: true,
                color: "#1AD9FF",
              }
            },
            data: scatterData
          }
        ]
      };

      this.myChart.setOption(option);
      this.myChart.on('click', this.onChartClick);
      this.myChart.on('mouseover', this.onChartMouseover);
      this.myChart.on('mouseout', this.onChartMouseout);
    }
  }
}
</script>

<style scoped lang="scss">

</style>

调整相关参数效果如图

总结

这个项目展示了如何使用 ECharts 创建复杂的 3D 地图可视化。通过多层叠加、精心设计的视觉效果和交互功能,可以将枯燥的地理数据转化为引人入胜的可视化作品。开发者可以根据这个模板,进一步扩展功能,如添加数据筛选、时间轴动画等高级特性。

Logo

DAMO开发者矩阵,由阿里巴巴达摩院和中国互联网协会联合发起,致力于探讨最前沿的技术趋势与应用成果,搭建高质量的交流与分享平台,推动技术创新与产业应用链接,围绕“人工智能与新型计算”构建开放共享的开发者生态。

更多推荐