在数据分析和展示领域,地理信息的可视化一直是一个重要且富有挑战性的方向。传统的表格或图表虽然能清晰呈现数据,但在展现地域分布特征时往往显得力不从心。而地图可视化不仅能直观反映数据的空间分布规律,还能通过交互操作深入探索不同层级的细节信息。例如,在经济指标、人员分布等场景中,地图可视化能够帮助用户快速识别热点区域和发展趋势。本文将介绍如何使用 HTML + ECharts.js 实现一个支持多级钻取的地图数据可视化系统。效果演示
页面启动后自动加载全国地图,并随机生成各省级行政区的模拟数据,右侧侧边栏同步显示排名前 15 的区域及其数值。点击地图上的某个省份,系统会动态加载该省的市级地图,并更新数据和侧边栏内容;继续点击某市,可进一步查看其区级数据。顶部面包屑记录访问路径,支持点击任意层级快速跳转。通过“返回上级”按钮逐步退回上一层级,直至回到全国视图。点击“刷新数据”按钮可重新生成当前层级的模拟数据,验证交互逻辑的稳定性。
页面结构
头部导航栏
头部固定在页面顶部,包含标题、面包屑导航和控制按钮。其中,“返回上级”按钮用于回退至上一层级,“刷新数据”按钮则触发当前层级的数据更新。<div class="header"> <div class="title">地图数据可视化</div> <div class="breadcrumb" id="breadcrumb"> <span class="breadcrumb-item active" data-level="0">全国</span> </div> <div class="controls"> <button class="btn" id="btn-reset" disabled>返回上级</button> <button class="btn btn-primary" id="btn-refresh">刷新数据</button> </div></div>
地图区域
地图容器占据了页面的主要空间,其中 #map-chart 是 ECharts 实例的挂载点。加载遮罩层在数据请求期间显示旋转动画和提示文本。<div class="map-container"> <div id="map-chart"></div> <div class="loading-overlay" id="loading"> <div class="spinner"></div> <div class="loading-text">正在加载地图数据...</div> </div></div>
右侧信息面板
右侧面板分为统计概览和区域排行两部分。统计卡片展示当前层级的总数、平均值、最大值和最小值等关键指标;区域列表则按数值大小排序显示前15名的具体数据。<div class="sidebar"> <div class="panel"> <div class="panel-title">数据统计</div> <div class="stat-grid"> <div class="stat-card"> <div class="stat-value" id="stat-count">34</div> <div class="stat-label" id="stat-label">省级行政区</div> </div> </div> </div>
<div class="panel"> <div class="panel-title">区域排行</div> <div class="region-list" id="region-list"></div> </div></div>
核心功能实现
地图数据加载与缓存机制
系统的核心在于高效地加载和管理多层级地理数据。通过建立 geoJsonCache 对象缓存已加载的地图数据,避免重复请求相同区域的 GeoJSON 文件。let geoJsonCache = {};let mapData = {};
async function loadChinaMap() { try { showLoading(); const response = await fetch('https://geo.datav.aliyun.com/areas_v3/bound/100000_full.json'); const geoJson = await response.json();
geoJsonCache['china'] = geoJson; echarts.registerMap('china', geoJson);
const provinceNames = geoJson.features.map(f => f.properties.name).filter(n => n); const { data, values } = generateData(provinceNames.length, provinceNames);
mapData['china'] = data; currentLevel = 0; history = [{ name: 'china', title: '全国', data: data }];
renderMap('china', data, '全国数据分布'); updateSidebar(data, '省级行政区', 34); updateStats(values, 34, '省级行政区'); updateBreadcrumb(['全国']);
hideLoading(); } catch (error) { console.error('加载地图失败:', error); hideLoading(); }}
多级钻取交互逻辑
系统支持从全国到省、市、区县的无缝钻取。每次点击地图区域都会触发相应级别的数据加载和视图渲染。当用户点击地图区域时,handleRegionClick 函数根据当前层级决定下一步动作:零级加载省级数据,一级加载市级数据,二级及以上则只高亮显示。function handleRegionClick(name) { if (currentLevel === 0) { loadProvinceMap(name); } else if (currentLevel === 1) { const currentMap = history[history.length - 1]; loadCityMap(name, currentMap.name); } else { myChart.dispatchAction({ type: 'highlight', name: name }); setTimeout(() => { myChart.dispatchAction({ type: 'downplay', name: name }); }, 2000); }}
myChart.on('click', function(params) { if (params.name && params.name !== '南海诸岛') { handleRegionClick(params.name); }});
ECharts 配置
ECharts 的配置对象负责将这些数据转化为美观的可视化图表。完整的地图选项包括交互提示框、地理坐标系设置和动画效果。其中 visualMap 组件定义了颜色映射规则。const option = { backgroundColor: '#fff', title: { text: title, left: 'center', top: 16, textStyle: { color: '#262626', fontSize: 18, fontWeight: 500 } }, tooltip: { trigger: 'item', backgroundColor: '#fff', borderColor: '#e8e8e8', borderWidth: 1, textStyle: { color: '#262626' }, extraCssText: 'box-shadow: 0 4px 14px rgba(0,0,0,0.1);', formatter: function(params) { if (!params.value) return params.name; return `<div style="padding: 8px;"> <div style="font-weight: 600; margin-bottom: 4px; color: #262626;">${params.name}</div> <div style="color: #595959;">数值: <span style="font-weight: 600; color: #1890ff;">${params.value.toLocaleString()}</span></div> ${currentLevel < 2 ? '<div style="color: #8c8c8c; margin-top: 4px;">点击查看详情</div>' : ''} </div>`; } }, };
myChart.setOption(option, true);
visualMap: { min: 0, max: 16000, left: 20, bottom: 20, text: ['高', '低'], textStyle: { color: '#595959' }, calculable: true, inRange: { color: ['#b8d4f3', '#9ac8f0', '#7cbced', '#5eb0ea', '#40a9ff', '#347cdc'] }, pieces: [ {min: 12000, label: '> 12000', color: '#347cdc'}, {min: 9000, max: 12000, label: '9000 - 12000', color: '#40a9ff'}, {min: 6000, max: 9000, label: '6000 - 9000', color: '#5eb0ea'}, {min: 3000, max: 6000, label: '3000 - 6000', color: '#7cbced'}, {max: 3000, label: '< 3000', color: '#b8d4f3'} ], outOfRange: { color: '#d9d9d9' }}
数字动画效果实现
为了让统计数据的变化更加生动,采用了基于时间插值的数字动画算法。该函数利用 requestAnimationFrame 实现60FPS的流畅动画。通过三次贝塞尔缓动函数让数字变化呈现出先快后慢的自然效果,避免线性过渡带来的机械感。
function animateNumber(id, target) { const element = document.getElementById(id); const start = parseInt(element.textContent.replace(/,/g, '')) || 0; const duration = 600; const startTime = performance.now();
function update(currentTime) { const elapsed = currentTime - startTime; const progress = Math.min(elapsed / duration, 1); const easeProgress = 1 - Math.pow(1 - progress, 3); const current = Math.floor(start + (target - start) * easeProgress);
element.textContent = current.toLocaleString();
if (progress < 1) { requestAnimationFrame(update); } }
requestAnimationFrame(update);}
源码地址
git地址:https://gitee.com/ironpro/chart-demo/blob/master/map-chart/index.html
阅读原文:
该文章在 2026/2/5 11:53:03 编辑过