基于Vue3+ECharts实现动态地图切换与平滑过渡的动画效果
作者:CF14年老兵
本文基于Vue3与ECharts5,实现市级地图展示及区县视图切换,通过universalTransition和Canvas图像处理技术,打造平滑动画与区域高亮效果,结合响应式布局与交互优化提升用户体验,需要的朋友可以参考下
本文将详细解析如何使用 Vue3 和 ECharts 实现市级地图展示功能,并添加地图切换时的平滑过渡动画效果。
- 使用 ECharts 的
universalTransition
实现地图切换动画 - 通过 Canvas 动态处理图像实现区域高亮效果
- 设计响应式布局确保地图适应不同屏幕尺寸
- 实现层级导航和交互反馈提升用户体验
功能概述
本组件实现了一个具有以下特性的地图展示系统:
- 展示河南省周口市行政区划地图
- 点击地图区域可切换到区县级别视图
- 地图切换时带有平滑的过渡动画
- 自定义地图样式和交互效果
- 响应式布局适应不同屏幕尺寸
技术栈
- Vue3(Composition API)
- ECharts 5
- TypeScript
- Canvas 图像处理
核心功能实现
1. 组件结构与依赖
<template> <div id="mapChart" class="mt-4 w-full" ref="mapChartRef"></div> </template> <script setup lang="ts"> import * as echarts from 'echarts'; import mapBgImgSrc from '@/assets/images/map-bg.png'; import geoData from '@/assets/jeo/jeoMap.json'; import { ref, onMounted, nextTick } from 'vue'; // 组件引用和数据 const mapChartRef = ref<HTMLElement | null>(null); const selectDistrictVal = ref('zhoukou'); // 当前选中的地区 let myChart: echarts.ECharts | null = null; // ECharts实例
2. 动态创建遮罩背景图
// 创建带遮罩的背景图像(用于高亮区域) function createMaskedBgImg(src: string, maskColor = 'rgba(0,0,0,0.1)'): Promise<string> { return new Promise((resolve) => { const img = new Image(); img.src = src; const canvas = document.createElement('canvas'); img.onload = () => { canvas.width = img.width; canvas.height = img.height; const ctx = canvas.getContext('2d')!; // 绘制原始图像 ctx.drawImage(img, 0, 0); // 添加遮罩层 ctx.fillStyle = maskColor; ctx.fillRect(0, 0, img.width, img.height); resolve(canvas.toDataURL()); }; img.onerror = () => resolve(src); // 失败时返回原图 }); }
3. 地图配置生成器
// 生成ECharts地图配置 const getMapOption = (mapType = 'zhoukou', maskedBgImg = mapBgImgSrc) => { // 从GeoJSON数据中提取当前区域 const feature = geoData.features.find( (f: any) => f.properties.name === mapType ); const mapGeoData = mapType === 'zhoukou' ? geoData : { type: 'FeatureCollection', features: feature ? [feature] : [] }; // 注册地图数据 echarts.registerMap('zhoukou', mapGeoData as any); return { // 动画配置 animation: true, animationDuration: 800, animationEasing: 'cubicOut' as any, animationDurationUpdate: 800, animationEasingUpdate: 'cubicOut' as any, // 标题 title: { top: 10, text: mapType === 'zhoukou' ? '河南省 - 周口市' : `周口市 - ${mapType}`, x: 'center', textStyle: { color: '#2564AD', fontWeight: 600, fontSize: 16 } }, // 地理坐标系配置 geo: { map: 'zhoukou', aspectScale: 0.8, layoutCenter: ['50.6%', '51%'], layoutSize: '91.5%', roam: false, z: 0, itemStyle: { areaColor: '#2564AD', borderColor: '#2564AD', borderWidth: 1 } }, // 地图系列 series: [{ type: 'map', map: 'zhoukou', universalTransition: { enabled: true, divideShape: 'clone' // 关键:启用形状分割动画 }, zoom: 1.2, itemStyle: { areaColor: { image: mapBgImgSrc, repeat: 'repeat' }, borderColor: '#80AACC', borderWidth: 2 }, emphasis: { // 鼠标悬停样式 itemStyle: { areaColor: { image: maskedBgImg, repeat: 'repeat' }, borderColor: '#80AACC' }, label: { color: '#409eff', fontWeight: 'bold' } }, select: { // 选中区域样式 itemStyle: { areaColor: { image: maskedBgImg, repeat: 'repeat' }, borderColor: '#80AACC' }, label: { color: '#409eff', fontWeight: 'bold' } }, label: { // 区域标签 show: true, color: '#80AACC', fontWeight: 'bold' } }] }; };
4. 地图初始化与交互
// 初始化地图 const initMap = async () => { if (!mapChartRef.value) return; // 创建ECharts实例 myChart = echarts.init(mapChartRef.value); // 创建遮罩背景图 const maskedBgImg = await createMaskedBgImg( mapBgImgSrc, 'rgba(0,0,0,0.1)' ); // 设置初始配置 myChart.setOption(getMapOption('zhoukou', maskedBgImg), false); // 添加点击事件处理 myChart.on('click', function (e: any) { if (!e.name || typeof e.name !== 'string') return; // 只有在地级市视图时才能点击进入区县 if (selectDistrictVal.value === 'zhoukou') { myChart.setOption(getMapOption(e.name, maskedBgImg), false); selectDistrictVal.value = e.name; } }); }; // 响应式调整地图尺寸 const setMapView = async () => { await nextTick(); if (mapChartRef.value?.parentNode) { const parentHeight = mapChartRef.value.parentNode.offsetHeight; const siblingHeight = mapChartRef.value.previousSibling?.offsetHeight || 0; mapChartRef.value.style.height = `${parentHeight - siblingHeight - 16}px`; } // 窗口大小变化时重绘 window.addEventListener('resize', () => { myChart?.resize(); }); }; // 组件挂载时初始化 onMounted(async () => { await setMapView(); initMap(); }); // 暴露外部控制方法 defineExpose({ getCurrentDistrict: () => selectDistrictVal.value, setDistrict: async (district: string) => { selectDistrictVal.value = district; const maskedBgImg = await createMaskedBgImg( mapBgImgSrc, 'rgba(0,0,0,0.1)' ); myChart?.setOption(getMapOption(district, maskedBgImg), false); } }); </script>
关键技术与优化点
1. 平滑过渡动画实现
通过以下配置实现地图切换时的平滑动画效果:
// 关键动画配置 universalTransition: { enabled: true, divideShape: 'clone' // 形状分割动画 }, animationDuration: 800, animationEasing: 'cubicOut', animationDurationUpdate: 800, animationEasingUpdate: 'cubicOut'
universalTransition
是 ECharts 5 引入的强大功能,它允许在数据更新时自动生成过渡动画,特别适合地理区域的切换。
2. Canvas 动态图像处理
使用 Canvas 动态生成带遮罩的背景图,实现区域高亮效果:
// 创建带遮罩的背景图 ctx.drawImage(img, 0, 0); // 绘制原图 ctx.fillStyle = maskColor; // 设置遮罩颜色 ctx.fillRect(0, 0, img.width, img.height); // 绘制遮罩层
3. 响应式布局处理
// 动态计算地图容器高度 const parentHeight = mapChartRef.value.parentNode.offsetHeight; const siblingHeight = mapChartRef.value.previousSibling?.offsetHeight || 0; mapChartRef.value.style.height = `${parentHeight - siblingHeight - 16}px`; // 监听窗口大小变化 window.addEventListener('resize', () => { myChart?.resize(); });
4. 交互体验优化
- 层级导航:只能从市级视图进入区县视图,防止无限深入
- 视觉反馈:悬停和选中状态有明显样式变化
- 标题动态更新:根据当前视图级别显示不同标题
- 性能优化:使用
false
参数避免不必要的重绘
myChart.setOption(getMapOption(e.name, maskedBgImg), false);
使用示例
在父组件中使用地图组件
<template> <div class="container"> <h1>河南省行政区划地图</h1> <div class="controls"> <button @click="backToCity">返回市级视图</button> <select v-model="selectedDistrict" @change="changeDistrict"> <option value="zhoukou">周口市</option> <option value="taikang">太康县</option> <option value="huaiyang">淮阳县</option> <!-- 其他区县选项 --> </select> </div> <MapComponent ref="mapRef" /> </div> </template> <script setup lang="ts"> import { ref } from 'vue'; import MapComponent from './MapComponent.vue'; const mapRef = ref(); const selectedDistrict = ref('zhoukou'); // 切换区县 const changeDistrict = () => { mapRef.value.setDistrict(selectedDistrict.value); }; // 返回市级视图 const backToCity = () => { selectedDistrict.value = 'zhoukou'; mapRef.value.setDistrict('zhoukou'); }; </script>
总结
以上就是基于Vue3+ECharts实现动态地图切换与平滑过渡的动画效果的详细内容,更多关于Vue3 ECharts地图切换与平滑过渡的资料请关注脚本之家其它相关文章!