ARTICLE DETAIL

资讯详情

深耕网站建设与运营推广的一线实战洞察。

ECharts地图3D感实战:伪3D视觉与真3D geo3D配置指南

ECharts地图3D感实战:伪3D视觉与真3D geo3D配置指南 ECharts地图做3D感这个问题我被问过太多次了。做数据大屏的、做政务可视化项目的、甚至做内部管理后台的都会拿着同一张平铺的中国地图过来问怎么让它看起来不那么平、不那么丑最好有点3D的感觉。这里说的3D感其实是个很宽泛的概念有人想要的是真正可以旋转的立体地图有人只是想让平面的中国地图看起来有层次、有质感。这两种需求对应的完全是两条技术路线很多教程上来就甩一个echarts-gl的geo3D配置结果项目里根本用不上或者性能扛不住。这篇就按我实际项目里的做法把两条路线都讲清楚配好可直接用的代码重点分享那些文档里不会写的坑。1. 先搞明白你要的是真3D还是看起来像3D很多同学一上来就问ECharts地图怎么3D但真做的时候才发现需求方的3D和你理解的3D根本不是一回事。我一般接需求时会先问清楚三句话地图能不能拖拽旋转上面还要不要叠别的图表页面打开的设备是什么这三句话基本决定了我走哪条路。如果需求是地图要能转、要有立体地形感、展示效果越炫越好那属于真3D得用echarts-gl扩展包里的geo3D组件。但真3D有个绕不开的问题性能开销大地图数据复杂时帧率很难看而且拖拽交互在大屏的触控设备上体验一般。如果你的项目背景是企业大屏大部分时候我建议别碰真3D用纯ECharts的2D地图组件做视觉欺骗就够了。所谓伪3D或者说视觉3D核心思路就是利用阴影、渐变、描边、分层这几样东西让人眼产生立体错觉。这个方案不需要引入任何额外依赖纯ECharts就能实现运行效率和普通2D地图完全一样。实际效果做出来在大屏上看跟真3D的观感差距并不大尤其是深色科技风主题下伪3D反而更干净、更好配周围的其他图表。我把两条路线的特点整理了一下接需求时可以直接对照对比维度伪3D纯ECharts真3Decharts-gl geo3D额外依赖无需要引入 echarts-gl地图交互支持缩放、拖拽平移支持旋转、缩放、拖拽性能开销低和普通2D地图一致高多边形多时明显掉帧视觉风格科技感、干净、好配图表立体感强、适合单独展示数据叠加可以正常叠加散点、飞线、柱状叠加方式受限配置复杂适用场景数据大屏、管理后台、报表演示页、主视觉大屏、宣传站2. 伪3D视觉的核心四个要素一个都不能少2.1 阴影参数让地图浮起来伪3D的第一个关键就是给每个区域块加投影。ECharts的地图series是基于SVG或Canvas绘制的itemStyle里支持阴影相关配置很多人不知道或者忽略了这一组参数。最基础的写法是这样itemStyle: { areaColor: #1e3a8a, borderColor: #60a5fa, borderWidth: 0.6, shadowColor: rgba(0, 0, 0, 0.6), shadowBlur: 12, shadowOffsetX: 0, shadowOffsetY: 8 }shadowOffsetY是向下偏移的阴影距离它决定了地图离地高度的错觉。偏移量越大块和块之间的悬浮感越强但也不能无限加大我一般控制在6到10之间再大阴影会糊成一片。shadowBlur是阴影的模糊程度相当于影子的软硬10到15比较合适太小的数字阴影会很生硬看起来像贴纸而不是立体块。这里有个容易踩的坑阴影只有在背景不是纯白的情况下才会明显。如果你页面背景是白色灰色阴影在浅色背景上几乎看不出来。所以做伪3D地图一定要搭配深色背景或者至少给地图容器区域一个深色底。这也是为什么你看到的所有科技感大屏地图基本都长在深蓝黑背景上这既是审美习惯也是技术上的必要选择。2.2 渐变填充制造鼓起来的曲面感阴影解决的是块与块之间的层次但单个区域内部如果是一块纯色看起来还是平的。想要每个省份有鼓起来的曲面感就得用线性渐变。ECharts里渐变只能用gbraphic对象来构造areaColor: new echarts.graphic.LinearGradient(0, 0, 0, 1, [ { offset: 0, color: #3b82f6 }, { offset: 1, color: #1e3a8a } ])关键在这个渐变的四个参数前两个是渐变起点的x、y坐标后两个是终点的x、y坐标取值都是0到1。我用(0, 0, 0, 1)表示从区域顶部到底部垂直渐变顶部亮、底部暗模拟光线从上方照射下来的效果。这样每个区域内部就有了从亮到暗的过渡视觉上自然产生曲面感。颜色搭配上我推荐一个稳妥方案亮部颜色和暗部颜色选同一个色相亮部亮度高一些、饱和度适中暗部更深更饱和。比如蓝色系就有大量现成组合顶部#3b82f6配底部#1e3a8a或者顶部#2dd4bf配底部#0f766e这种青绿色系。切忌亮部用很浅的白蓝、暗部用很深的藏蓝对比太极端会显得脏像地图被烧焦了一样。还有个小技巧渐变的方向不一定要垂直也可以配合光线的位置做45度角渐变。比如页面左上角有标题、视觉重心在左侧时用(0, 0, 1, 1)方向渐变左上到右下光影会更自然。这个看整体页面的光源设定没有绝对标准。2.3 描边和强调态让边界立起来光影有了之后区域与区域之间的边界处理直接决定立体感的成败。类地图默认的描边是细黑边或者浅灰边这在立体风格下很出戏。我一般把描边做成中等亮度的同色系亮色模拟物体棱边的反光。borderColor: #7dd3fc, borderWidth: 0.8描边宽度不要超过1像素太宽会变成明显的外轮廓线地图瞬间从立体块退化成卡通拼图。描边颜色要跟区域主色是同一色系、但更亮一档比如主体是蓝色就用浅蓝描边主体是绿色就用浅绿描边。同时不要忘了emphasis状态。鼠标悬浮时的效果是增加立体感的重要加分项默认的悬浮效果只是换个高亮颜色太单薄。我通常这样配置emphasis: { itemStyle: { areaColor: #60a5fa, shadowColor: rgba(96, 165, 250, 0.6), shadowBlur: 24, shadowOffsetY: 10 }, label: { show: true, color: #fff, fontSize: 12, fontWeight: bold } }悬浮时阴影换成与高亮色同色的光晕模糊值调大这块区域就像被提起来并且发光整个地图的立体交互感立刻上一个档次。这里有个细节悬浮光晕的颜色用白色也好看但白色光晕在浅色块上看不清同色系光晕更保险。2.4 辅助元素让空间关系更明显地图本身做好光影之后如果还觉得平问题往往不在图层而在于画面里缺少能暗示空间关系的参照物。这时候叠加effectScatter散点和飞线是最快的解法。series: [{ type: effectScatter, coordinateSystem: geo, data: [ { name: 北京, value: [116.4, 39.9, 100] }, { name: 上海, value: [121.47, 31.23, 80] } ], symbolSize: 8, rippleEffect: { brushType: stroke, scale: 4 }, itemStyle: { color: #fbbf24, shadowBlur: 10, shadowColor: #fbbf24 }, label: { show: true, position: right, formatter: {b}, color: #fff, fontSize: 10 } }]涟漪扩散的散点会让人眼优先把它理解为浮在三维空间上面的粒子从而不知不觉中认同整个场景是立体的。飞线则更直接一条带光晕的曲线从A点飞向B点本身就带有空间纵深含义。这一层叠加不需要很花哨散点选一两个亮色点缀飞线用细线加半透明拖尾就能明显增强整个地图的立体氛围。3. 实操一套可复用的伪3D中国地图配置3.1 准备阶段地图数据和基础容器开始写配置之前先把地图数据准备好。ECharts从5.0开始不再内置中国地图的GeoJSON需要自己准备。常用的获取渠道是阿里云DataV的GeoJSON接口返回的是标准GeoJSON直接拿来用就行。拿到数据后这样注册import chinaMap from ./china.json; echarts.registerMap(china, chinaMap);容器部分要注意的是地图组件的宽高会直接影响阴影和布局的观感容器不要给太高建议比例控制在16:7到16:9之间。地图显示太扁或太长立体感都会被拉伸变形。我习惯在容器里直接加一层深色渐变背景#map { width: 100%; height: 560px; background: linear-gradient(180deg, #0b1530 0%, #0f1f3d 100%); }3.2 核心配置代码可直接抄下面这份配置是我项目里反复用的基础模板做了伪3D的完整效果包括阴影、渐变、描边、悬浮光晕还有一个用geo坐标定位的散点层const option { backgroundColor: transparent, tooltip: { trigger: item, formatter: function (params) { if (params.seriesType effectScatter) { return params.name br/数值 params.value[2]; } return params.name br/ (params.value ? 数值 params.value : 暂无数据); } }, geo: { map: china, roam: true, zoom: 1.2, top: 40, left: 20, right: 20, bottom: 20, itemStyle: { areaColor: new echarts.graphic.LinearGradient(0, 0, 0, 1, [ { offset: 0, color: #3b82f6 }, { offset: 1, color: #172e6e } ]), borderColor: #7dd3fc, borderWidth: 0.8, shadowColor: rgba(0, 0, 0, 0.6), shadowBlur: 12, shadowOffsetY: 8 }, emphasis: { itemStyle: { areaColor: #60a5fa, shadowColor: rgba(96, 165, 250, 0.6), shadowBlur: 24, shadowOffsetY: 10 }, label: { show: true, color: #ffffff, fontSize: 12 } }, select: { itemStyle: { areaColor: #f59e0b }, label: { show: true, color: #ffffff } } }, series: [{ name: 重点城市, type: effectScatter, coordinateSystem: geo, data: [ { name: 北京, value: [116.4, 39.9, 100] }, { name: 上海, value: [121.47, 31.23, 95] }, { name: 广州, value: [113.26, 23.13, 90] }, { name: 成都, value: [104.06, 30.67, 85] }, { name: 武汉, value: [114.3, 30.6, 80] } ], symbolSize: 8, rippleEffect: { brushType: stroke, scale: 4 }, itemStyle: { color: #fbbf24, shadowBlur: 10, shadowColor: #fbbf24 }, label: { show: true, position: right, formatter: {b}, color: #e2e8f0, fontSize: 10 } }] };这份配置里有一个很多人会忽略的点地图上要显示中文省份名但普通状态下我把label关掉了只保留悬浮时显示。原因很简单中国地图34个省级区域所有名字都常驻显示的话字体小看不清字体大就互相遮挡整个地图看起来乱七八糟立体感会被文字信息冲淡。如果你的需求方坚持常显标签那就尽量只显示部分重点区域的标签用regions数组做选择性配置。3.3 区域高亮与数据联动伪3D地图做数据大屏时经常要根据业务数据给特定区域做高亮比如某个省份的销售额达到指标就让它亮起来。这个需求用regions配置实现regions: [{ name: 广东, itemStyle: { areaColor: new echarts.graphic.LinearGradient(0, 0, 0, 1, [ { offset: 0, color: #f59e0b }, { offset: 1, color: #b45309 } ]), shadowColor: rgba(245, 158, 11, 0.5), shadowBlur: 20, shadowOffsetY: 8 }, label: { show: true, color: #fff } }]把regions放进geo配置里对应区域会覆盖默认的itemStyle样式。这样高亮区域和其他区域之间既有颜色对比又有相同的阴影体系立体感是统一的不会出现一块很立体一块很平面的割裂感。数据联动方面我通常的做法是给地图series增加一个正常显示的map层单独用来承载数据值series: [{ name: 销售额, type: map, map: china, geoIndex: 0, data: [ { name: 北京, value: 1200 }, { name: 上海, value: 1100 } ] }]这里geoIndex: 0表示这个map系列绑定上面定义的geo组件这样散点层和地图层共享同一套坐标系和缩放状态不会出现地图拖拽了、散点还停在原位的情况。tooltip里的数据也主要靠这个map层来带。3.4 微调心得哪些参数值得花时间我调这类地图调多了发现花时间最值的是三个参数阴影偏移量、底色渐变的方向、以及散点的颜色和大小。这三个参数对整体观感的影响最大而且每次换主题色都要重新调。比如阴影偏移量蓝色系地图用8比较合适但如果你的主色是深灰或者黑金色系阴影偏移量可以适当加大到12因为深色地图的阴影在深色背景上视觉权重低不加大会显得糊在一起。底色渐变的方向我前面说过一般用垂直但如果你的大屏页面左右不对称比如左边有很大一块其他图表那地图的光源最好也向右倾用(0, 0, 1, 1)方向渐变这样整页的光影逻辑是统一的。散点颜色和大小更值得花时间因为它是整个地图上最跳的元素。我一般会让散点色跟主色形成互补关系蓝底地图配琥珀黄、金色散点绿底地图配亮紫或品红散点。symbolSize不要超过10太大像个贴纸糊在地图上。4. 进阶真3D地图的geo3D配置与避坑4.1 echarts-gl引入与基础配置如果确认需求方就是要那种能旋转、有立体高度的地图那就得用echarts-gl。引入方式很简单npm install echarts echarts-glimport * as echarts from echarts; import echarts-gl;引入之后geo3D是作为组件直接可用的。基础配置长这样const option { tooltip: {}, geo3D: { map: china, shading: lambert, regionHeight: 3, boxWidth: 100, boxDepth: 10, environment: rgba(0, 0, 0, 0), light: { main: { intensity: 1.2, shadow: true }, ambient: { intensity: 0.3 } }, viewControl: { distance: 100, alpha: 40, beta: 0, autoRotate: false, zoomSensitivity: 1.2 }, itemStyle: { color: #1e3a8a, opacity: 1 }, label: { show: false } } };这里重点说几个参数。regionHeight是每个区域块的高度单位是数值不是像素也不是真实地理高度它控制的是省份凸起的程度。我一般取2到4太小看不出立体太大地图看起来像牙膏不自然。注意geo3D里地图是全部区域一起凸起不是按数据值映射高度如果你的需求是按数据大小生成高低起伏的立体柱状地图需要的是bar3D配合地图坐标那是另一套方案了。4.2 光照和材质是效果的分水岭geo3D的立体感来自光照所以shading参数是关键。取值一般有color、lambert、realistic三种。color是纯色填充没有光影关系lambert是最常用的提供漫反射光影realistic是物理材质效果最好但性能开销大需要配合更多参数调节。我实际项目里用的最多的是lambert光照然后手动调光light: { main: { intensity: 1.2, shadow: true, alpha: 30, beta: 40 }, ambient: { intensity: 0.4 } }main光就是主光源alpha和beta决定光线照下来的角度默认是30和40这个角度会让地图产生明显的明暗面立体感最强。shadow: true会开启区域块之间的投影这是真3D才有的大杀器区块高的地方会在低的地方投下影子空间关系一下就真实了。但开了投影后渲染开销明显变大如果你的地图数据量很大建议把shadow关掉用明暗面对比来替代。环境光ambient用于补光避免背光面死黑一片。intensity建议在0.3到0.6之间太低地图背面完全看不清太高主光源产生的明暗对比就没了立体感又会被拉平。4.3 viewControl控制用户怎么看地图viewControl是geo3D的视图控制参数相当于一个虚拟相机。我重点提醒几个默认值的坑第一distance默认值在小容器里地图会非常大超出画面我一般先设100然后根据实际容器大小调整第二alpha是俯仰角40度左右是最舒服的俯瞰角度低于20度地图太侧、像贴在地面上高于70度又退化成俯视平面图了没立体感第三autoRotate默认是false但很多演示现场希望地图自己慢慢转我习惯把它设为true速度调到20左右转速太快的旋转会让人头晕也影响数据阅读。真3D地图和普通图表共存在同一个大屏上的时候还有个交互冲突需要注意geo3D默认支持鼠标拖拽旋转如果页面里有其他图表的tooltip、或者有tab切换用户操作时会经常误触到地图给拖转了体验很糟。我一般会根据场景设置rotateSensitivity传false直接关闭鼠标拖拽旋转只保留自动旋转和滚轮缩放需要调整视角时用viewControl里的参数写死。4.4 真3D的常见坑真3D地图完整跑起来不难但有几个坑我每做一个项目都会碰到。第一个是地图JSON的坐标系问题GeoJSON用的是经纬度坐标geo3D内部会把经纬度映射到3D平面上如果你的地图JSON有比较大的岛屿和边界细分点渲染出来会有明显的锯齿感。解决办法是在注册数据之前做一下坐标点抽稀把一些无用的过度点去掉既能减少锯齿又能提升性能。第二个坑是页面里同时用2D坐标系和3D坐标系时series配置会互相干扰。比如你有一个scatter系列是coordinateSystem: geo另一个是coordinateSystem: geo3D两个系列的视觉参数不能混用尤其是tooltip的trigger和formatter逻辑很容易在一个图例切换时把另一个的配置冲掉。遇到这类问题先检查是不是每个series都明确指定了coordinateSystem。第三个坑是移动端。真3D地图在移动端基本是灾难帧率低不说触控旋转和页面滚动的冲突也让人抓狂。如果是移动端报表我建议直接放弃真3D老老实实用伪3D方案。5. 常见问题与排查技巧5.1 问题速查表现象可能原因解决方案阴影完全看不到页面背景太亮或itemStyle没生效换深色背景确认配置在geo.itemStyle而非series.itemStyle地图数据加载后是空白的GeoJSON未注册或注册名为空检查registerMap的第一个参数与option里的map值是否一致悬浮时地图区域发白、光影消失emphasis里没有配置阴影和渐变在emphasis里同步配置shadowColor、shadowBlur、渐变地图太靠上或溢出容器没有设置top/left或zoom不合理用top/left/right/bottom约束范围用zoom控制比例echarts-gl引入页面报错echarts与echarts-gl版本不兼容使用echarts 5.x对应的最新echarts-gl 2.x地图和散点不联动map系列没有设置geoIndex在map系列中配置geoIndex: 0与geo组件关联区域标签全叠在一起label常显且字号过大关闭常显label只在emphasis时显示或用regions控制局部显示5.2 阴影不生效的排查思路阴影不生效是伪3D地图最常见的问题这里面有几种情况。第一种是配置写在了series里而不是geo里当你有多个map系列时阴影其实由geo组件的itemStyle决定series里的itemStyle负责的是数据映射后的颜色覆盖两者不冲突但生效层级有区别。第二种是阴影颜色透明度太低rgba(0, 0, 0, 0.6)这种看起来正常但有些项目因为整体调色会把图表区背景设置成灰色系这就悲剧了灰色背景配灰色阴影效果约等于零。这种时候把阴影颜色改成深蓝半透明比如rgba(2, 6, 23, 0.8)会明显很多。第三种情况是Canvas渲染模式下阴影效果有限。ECharts默认用Canvas渲染SVG渲染器对阴影的支持更完整如果你的地图没有特殊性能问题可以改用SVG渲染器试试const chart echarts.init(dom, null, { renderer: svg });不过SVG在区域数量特别多、频繁拖拽的时候性能又不如Canvas这个要取舍。我一般300个区域以内用SVG以上用Canvas。5.3 tooltip换行和自定义显示地图tooltip也是被问得多的点。默认的tooltip显示经纬度数值毫无意义一定要用自己的formatter。如果要在tooltip里换行用HTML的br标签就行tooltip: { trigger: item, formatter: function (params) { if (params.seriesType effectScatter) { return params.name br/指标值 params.value[2] br/备注重点监控; } return params.name br/数值 (params.value ? params.value : -); } }地图tooltip的默认样式很轻在深色背景上文字会看不清我习惯统一设置一个深色半透明背景、带细边框的样式tooltip: { backgroundColor: rgba(15, 23, 42, 0.9), borderColor: #60a5fa, borderWidth: 1, textStyle: { color: #e2e8f0, fontSize: 12 } }5.4 地图性能优化地图卡顿的根子基本都在GeoJSON数据量上。中国地图的完整GeoJSON可能有一两兆浏览器解析加绘制要花不少时间。优化手段就两个方向离线抽稀和分级加载。抽稀就是去掉坐标点冗余信息把多边形精度降低。很多在线工具可以做也可以用Turf.js这类库在项目里做。我一般把精度控制在边界线条肉眼看起来是光滑的这个程度就行不需要亚像素级的精度。一份原始1.8MB的中国地图抽稀到400KB以内绘制性能会提升一个档次而肉眼几乎看不出区别。分级加载是另一个思路先用一个低精度的全国地图做整体展示用户放大到一定程度时再切换到各省的精细地图。这个方案需要额外维护一套省界数据适合地图交互很重、需要下钻到市级区级的中大型项目。如果你只是做个大屏展示抽稀就够了。6. 一点私货我为什么偏爱伪3D最后再说点个人体会。做过的可视化项目里真正需要用真3D地图的其实很少。大部分业务场景要的是让地图看起来高级、立体、有科技感它上面还要放趋势折线图、排名柱状图、指标卡片真3D地图在这种拥挤的页面里反而容易变成噪音旋转的视角也会干扰其他图表的阅读。伪3D方案最大的优势是它跟其他图表的风格统一性。同一套阴影、渐变、光晕参数既可以给地图用也可以给柱状图、饼图、仪表盘用整个大屏的风格是完整的。而真3D的立体视觉很难延伸到普通2D图表上大概率页面上会出现地图很炫、其他图表很平的割裂感。如果你是新项目我建议先从伪3D方案起步把阴影、渐变、描边、散点这几板斧练熟确认需求方确实要那种可以旋转的立体效果时再上echarts-gl。毕竟改配置容易改技术方案伤筋动骨。做可视化跟做产品一样先解决主要矛盾再谈锦上添花。
返回列表