在 Vue 3 和 TypeScript 项目中实现 3D 地图展示既能为应用增添视觉冲击力,也能提升数据表达的丰富性。ECharts GL 提供的 geo3D
和 map3D
组件是实现这一功能的得力工具。下面我将为你解释它们的区别,并提供实现的思路、示例代码和实用建议。
🗺️ 使用 ECharts GL 在 Vue 3 中实现 3D 地图
✨ 效果展示
以下是通过 geo3D
实现的 3D 地图效果,支持旋转、缩放、区域高亮、数据点标记等交互功能。
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Vue 3 + ECharts GL 3D 地图示例</title>
<script src="https://cdn.jsdelivr.net/npm/echarts@5.4.3/dist/echarts.min.js"></script>
<script src="https://cdn.jsdelivr.net/npm/echarts-gl@2.0.9/dist/echarts-gl.min.js"></script>
<style>
#mapContainer {
width: 100%;
height: 600px;
}
</style>
</head>
<body>
<div id="mapContainer"></div>
<script>
// 初始化 ECharts 实例
const chartDom = document.getElementById('mapContainer');
const myChart = echarts.init(chartDom);
// 示例配置选项
const option = {
tooltip: {
trigger: 'item',
formatter: params => {
if (params.componentType === 'series' && params.seriesType === 'scatter3D') {
return `城市: ${params.data[3]}<br/>值: ${params.data[2]}`;
}
return params.name;
}
},
visualMap: {
show: true,
left: 'right',
min: 0,
max: 100,
inRange: {
color: ['#313695', '#4575b4', '#74add1', '#abd9e9', '#e0f3f8', '#ffffbf', '#fee090', '#fdae61', '#f46d43', '#d73027', '#a50026']
},
calculable: true,
textStyle: {
color: '#fff'
}
},
geo3D: {
map: 'china',
boxHeight: 5, // 地图区域的高度或厚度
regionHeight: 5, // 地图版块厚度
environment: 'auto',
itemStyle: {
color: 'rgba(95,158,160,0.5)', // 地图板块的颜色
opacity: 1, // 图形的不透明度 [default: 1]
borderWidth: 0.5, // 图形描边的宽度
borderColor: '#000' // 图形描边的颜色
},
emphasis: {
itemStyle: {
color: '#66ffff' // 高亮状态下的颜色
}
},
label: {
show: true,
color: '#000', // 标签颜色
fontSize: 8 // 字体大小
},
shading: 'lambert', // 着色方式,可选 'color', 'lambert', 'realistic'
light: {
main: {
intensity: 1.2, // 光照强度
shadow: false, // 是否显示阴影
alpha: 55, // 主光源绕 x 轴,即上下旋转的角度
beta: 10 // 主光源绕 y 轴,即左右旋转的角度
},
ambient: {
intensity: 0.5 // 环境光的强度
}
},
viewControl: {
projection: 'perspective', // 投影方式,默认为透视投影'perspective'
autoRotate: false, // 是否开启视角绕物体的自动旋转查看
autoRotateSpeed: 10, // 物体自传的速度
distance: 120, // 默认视角距离主体的距离
minDistance: 40, // 视角通过鼠标控制能拉近到主体的最小距离
maxDistance: 400, // 视角通过鼠标控制能拉远到主体的最大距离
alpha: 40, // 视角绕 x 轴,即上下旋转的角度
beta: 0, // 视角绕 y 轴,即左右旋转的角度
panMouseButton: 'left', // 平移操作使用的鼠标按键
rotateMouseButton: 'right' // 旋转操作使用的鼠标按键
}
},
series: [
{
type: 'scatter3D',
coordinateSystem: 'geo3D',
symbol: 'circle',
symbolSize: 10,
itemStyle: {
color: '#ff0000'
},
data: [
[116.46, 39.92, 50, '北京'],
[121.48, 31.22, 80, '上海'],
[114.07, 22.62, 30, '深圳'],
[113.53, 22.3, 40, '珠海'],
[113.23, 23.16, 60, '广州']
]
}
]
};
// 使用配置项和数据显示图表
myChart.setOption(option);
// 响应窗口大小变化
window.addEventListener('resize', function() {
myChart.resize();
});
</script>
</body>
</html>
📦 实现步骤
1. 环境搭建
首先安装必要的依赖:
npm install echarts echarts-gl
2. 准备地图数据
你需要获取相应区域的 GeoJSON 数据。可以从以下来源获取:
- 阿里云 DataV:http://datav.aliyun.com/portal/school/atlas/area_selector
- GitHub 资源:例如 https://github.com/Bing0915/city_geojson
3. Vue 3 组件封装 (Composition API + TypeScript)
创建一个可复用的 3D 地图组件 (Map3D.vue
):
<template>
<div ref="chartRef" class="map-3d-container"></div>
</template>
<script setup lang="ts">
import { ref, onMounted, onUnmounted, watch } from 'vue';
import * as echarts from 'echarts';
import 'echarts-gl';
import { ECharts, EChartsOption } from 'echarts';
// 定义组件接收的 props
interface Map3DProps {
mapData: any; // GeoJSON 数据
mapName?: string; // 注册的地图名称
option?: EChartsOption; // 可自定义的 ECharts 配置项
}
const props = withDefaults(defineProps<Map3DProps>(), {
mapName: 'myMap',
});
const chartRef = ref<HTMLElement>();
let chartInstance: ECharts | null = null;
// 初始化图表
const initChart = () => {
if (!chartRef.value) return;
chartInstance = echarts.init(chartRef.value);
// 注册地图
echarts.registerMap(props.mapName, props.mapData);
const defaultOption: EChartsOption = {
tooltip: {
trigger: 'item',
formatter: (params: any) => {
if (params.componentType === 'series' && params.seriesType === 'scatter3D') {
return `城市: ${params.data[3]}<br/>值: ${params.data[2]}`;
}
return params.name;
}
},
geo3D: {
map: props.mapName,
boxHeight: 5,
regionHeight: 5,
itemStyle: {
color: 'rgba(95,158,160,0.5)',
borderWidth: 0.5,
borderColor: '#000'
},
emphasis: {
itemStyle: {
color: '#66ffff'
}
},
label: {
show: true,
color: '#000',
fontSize: 8
},
shading: 'lambert',
viewControl: {
distance: 120,
alpha: 40,
beta: 0,
panMouseButton: 'left',
rotateMouseButton: 'right'
}
}
};
// 合并默认配置和传入的配置
const finalOption = { ...defaultOption, ...props.option };
chartInstance.setOption(finalOption);
};
// 响应窗口变化
const handleResize = () => {
chartInstance?.resize();
};
onMounted(() => {
initChart();
window.addEventListener('resize', handleResize);
});
onUnmounted(() => {
window.removeEventListener('resize', handleResize);
chartInstance?.dispose();
});
// 监听 props 变化
watch(() => props.mapData, () => {
if (chartInstance) {
initChart();
}
});
// 暴露方法或实例给父组件 (如果需要)
defineExpose({
getInstance: () => chartInstance
});
</script>
<style scoped>
.map-3d-container {
width: 100%;
height: 600px;
}
</style>
4. 在父组件中使用
<template>
<div>
<Map3D :mapData="chinaJson" :mapName="china" :option="customOption" />
</div>
</template>
<script setup lang="ts">
import { ref } from 'vue';
import Map3D from '@/components/Map3D.vue';
import chinaJson from '@/assets/json/china.json'; // 导入 GeoJSON 数据
// 可以自定义配置覆盖默认配置
const customOption = ref({
geo3D: {
itemStyle: {
color: 'rgba(25, 100, 150, 0.7)' // 自定义颜色
}
},
series: [
{
type: 'scatter3D',
coordinateSystem: 'geo3D',
symbol: 'pin',
symbolSize: 20,
itemStyle: {
color: '#ff0000'
},
data: [
[116.46, 39.92, 50, '北京'],
[121.48, 31.22, 80, '上海'],
[114.07, 22.62, 30, '深圳']
]
}
]
});
</script>
💡 高级功能与技巧
-
添加 3D 柱图 (bar3D) 你可以在地图上叠加 3D 柱图来表示不同区域的数据大小。
// 在 option.series 中添加 { type: 'bar3D', coordinateSystem: 'geo3D', shading: 'lambert', barSize: 1, // 柱子大小 minHeight: 0.5, // 最小高度 itemStyle: { color: '#5E5FFF' }, data: originalDatas.datas.map((item) => { // 将数据转换为 bar3D 需要的格式 [经度, 纬度, 值] return [item.lng, item.lat, item.value]; }) }
-
使用纹理贴图 通过
realisticMaterial.detailTexture
可以为地图表面添加纹理,增强质感。geo3D: { // ... 其他配置 shading: 'realistic', realisticMaterial: { detailTexture: '/path/to/texture.png', // 纹理贴图路径 textureTiling: 1 // 纹理平铺 } }
-
处理点击事件与高亮 为图表添加点击事件,并实现区域高亮效果。
// 在初始化图表后 chartInstance.on('click', (params: any) => { console.log('点击了:', params.name); // 可以通过 dispatchAction 高亮区域 chartInstance.dispatchAction({ type: 'highlight', seriesIndex: 0, name: params.name }); });
⚠️ 注意事项
- 版本兼容性: 确保
echarts
和echarts-gl
版本兼容。较新的echarts
(如 v5.4.3) 配合较新的echarts-gl
(如 v2.0.9) 通常较好。 - 性能考虑: 3D 地图对性能要求较高,数据量大时注意优化(如减少不必要的视觉元素、简化几何体)。
- 地图数据: 确保 GeoJSON 数据准确,并正确注册到 ECharts。
- TypeScript 类型: 由于 ECharts GL 的类型定义可能不如 ECharts 核心库完善,有时可能需要
// @ts-ignore
或自行补充类型定义。