echarts-gl 3D可视化实战:从柱状图到地球大屏

发布时间:2026/10/15 18:13:41
echarts-gl 3D可视化实战:从柱状图到地球大屏 最近在做部门里的经营数据大屏业务侧丢过来八维指标2D堆叠图直接糊成了意大利面。领导一句话就把“3D效果”变成了硬需求。我的第一反应是上Three.js但排期实在不等人后来想起 ECharts 生态里还有一套 WebGL 扩展库echarts-gl配置风格和 ECharts 一脉相承于是花了两个晚上把核心场景完整跑了一遍。这篇“初体验”就是我踩坑之后整理出来的完整记录覆盖了选型思路、配置细节、常见报错和性能优化适合正在评估 3D 可视化方案、或者准备在业务大屏里接入 echarts-gl 的开发者参考。先说结论echarts-gl 不是万能药但如果你已经熟练使用 ECharts它能用一个极低的学习成本把 3D 柱状图、3D 散点图、3D 曲面、地球、地图飞线这些效果全部落地。它解决的最大痛点是“配置式 3D”而不是“编码式 3D” —— 你不需要关心相机矩阵、灯光模型、顶点缓冲这些底层概念只需要像写普通 ECharts option 一样描述图形、数据、交互即可。这篇内容我也会把数据和代码全部模拟化处理方便你直接照搬思路。1. 为什么在可视化选型时选了 echarts-gl1.1 3D 可视化库的现状与选型权衡在真正动手之前我简单对比过市面上主流的 3D 方案。Three.js 功能最强理论上什么都能画但项目里要自己管理场景、材质、加载器、动画循环如果只是做数据展示成本偏高Babylon.js 偏游戏引擎方向物理效果和复杂交互很强但业务图表场景用不上那么多东西deck.gl 主打大数据量图层渲染性能确实猛但需要接受它的图层编程模型和 ECharts 混排图表时要把两套体系串起来。echarts-gl 的场景定位非常清晰它是 ECharts 的官方扩展TypeScript 类型、事件机制、渐变颜色、数据映射这些能力全盘继承学习成本几乎为零。如果你已经熟悉 series-bar那换成 series-bar3D 只是把坐标系和几个参数改一下。这种“无缝衔接”的体验在赶工期的时候价值极大。我们最终拍板选它还有一个原因是团队现有图表全部跑在 ECharts 上引入 echarts-gl 不会造成技术栈割裂后续维护成本最低。1.2 能力边界它能做什么不能做什么我实际跑通的主要场景有 6 类这里先做一个能力速览后面章节会展开细说。场景对应系列适合表达的信息3D 柱状图bar3D三维分布、对比排序、多维度排名3D 散点图scatter3D聚类分布、异常点、多变量关联3D 曲面surface地形模拟、函数曲面、响应面分析3D 地图map3D / geo3D区域分布、地理聚合、飞线动画3D 地球globe全球视角、跨区域链路、热力覆盖3D 流场flowGL向量场、风力/水流、路径密度它的短板也必须提前讲清楚交互能力相比 Three.js 弱很多。echarts-gl 提供视角旋转和平移但如果你要做“喷泉一样的物理粒子”“实时 3D 建模编辑”这类重度定制它并不合适。它更适合“把已有 2D 图表做维度升维”这件事。我们这次大屏的所有页面都是围绕这些能力搭起来的。2. 初始环境搭建与核心概念理解2.1 安装引入一把梭还是分步走我使用的是 ECharts 5 版本echarts-gl 对应版本也跑得很顺。安装方式非常直接npm install echarts echarts-gl然后在入口文件里引入注册import * as echarts from echarts; import echarts-gl;这里的import echarts-gl是副作用引入它会自动往 echarts 上注册所有 3D 系列。如果你担心打包体积可以考虑按需注册的方式但 echarts-gl 目前主路径还是整体引入对首屏体积敏感的项目建议做动态 import只在进入 3D 页面时才加载这个模块。一个很容易忽略的检查点是 WebGL 环境。3D 渲染依赖 canvas 的 WebGL 上下文如果用户的浏览器关闭了硬件加速或者显卡驱动有问题页面会直接白屏。我在接入阶段写了一个简单的能力检测函数放在应用启动时执行。function checkWebGL() { try { const canvas document.createElement(canvas); return !!(window.WebGLRenderingContext canvas.getContext(webgl)); } catch (e) { return false; } }检测不通过就渲染一张提示图的兜底页面避免业务方看到一片空白。这种处理在大屏项目里非常加分。2.2 坐标系、视图控制器与光照三条主线理解 echarts-gl 的配置项基本可以按三条主线拆解。第一条是坐标系。普通 ECharts 用 xAxis/yAxis 表示平面坐标系echarts-gl 引入了 grid3D、geo3D、globe 三类 3D 坐标系。bar3D 和 scatter3D 默认挂在 grid3D 下地图和飞线挂在 geo3D 或 globe 下。你选的坐标系决定了系列的位置语义这是整个配置的地基。第二条是视图控制器。配置项集中在viewControl里包括alpha垂直视角、beta水平旋转角、distance相机距离、autoRotate自动旋转、animationDuration旋转动画时长。它控制的是相机怎么围绕场景转不是数据本身很多人调了半天视角不变就是把这个配置放错了位置。第三条是光照和后期。3D 图形如果没有光影看起来就是扁平色块立体感全靠component.light里的主光源和环境光撑起来。后期效果则集中在postEffect中比如bloom泛光、depthOfField景深、SSAO环境光遮蔽。大屏场景最常用的是 bloom开完之后柱子和曲面边缘会有发光感视觉层次立刻不一样。注意postEffect 里的效果不是全部默认开启必须有开启配置才会生效。性能弱的目标设备上SSAO 建议直接关掉它非常消耗 GPU。3. 从 0 到 1第一个可交互的 3D 柱状图3.1 核心配置从 2D 思维切换到 3D 思维我先用最经典的 bar3D 举例。这个系列的数据结构是一个个三维点每一项是[x, y, z]其中 x 和 y 是平面位置z 是高度。下面是一组完全模拟的数据用月份作为 x 轴用产品线作为 y 轴用销量作为 z 轴。const option { tooltip: {}, visualMap: { max: 1200, inRange: { color: [#313695, #4575b4, #74add1, #abd9e9, #fee090, #fdae61, #f46d43, #d73027] } }, xAxis3D: { type: category, data: [一月, 二月, 三月, 四月, 五月, 六月] }, yAxis3D: { type: category, data: [产品A, 产品B, 产品C] }, zAxis3D: { type: value }, grid3D: { boxWidth: 200, boxDepth: 100, axisLabel: { textStyle: { color: #fff } }, viewControl: { alpha: 25, beta: 40, distance: 220, autoRotate: true, autoRotateSpeed: 6 }, light: { main: { intensity: 1.2, shadow: true }, ambient: { intensity: 0.3 } } }, series: [{ type: bar3D, data: [ [0, 0, 320], [1, 0, 540], [2, 0, 760], [3, 0, 410], [4, 0, 880], [5, 0, 1030], [0, 1, 510], [1, 1, 630], [2, 1, 810], [3, 1, 520], [4, 1, 980], [5, 1, 1160], [0, 2, 260], [1, 2, 380], [2, 2, 490], [3, 2, 340], [4, 2, 620], [5, 2, 720] ], shading: realistic, barSize: 12, emphasis: { label: { show: true, textStyle: { color: #fff, fontSize: 12 } } } }] };把这段配置塞进一个容器初始化就能看到 3D 柱状图在自动旋转鼠标拖拽可以改变视角滚轮可以缩放。柱子的高度、颜色梯度、阴影关系全部自动映射出来。最直观的差异是二维图表里颜色只能表达一个额外维度而三维里位置本身又多了一维。3.2 高度、间距与颜色的“为什么”这套配置里几个参数我会单独解释因为它们直接决定图表的观感和可用性。boxWidth和boxDepth是 grid3D 坐标系底面的大小。我试过把 boxWidth 设得过大柱体之间会空隙巨大视觉上非常松垮设得太小数据点就挤成一团。经验值是让 boxWidth 按 x 轴分类数的三倍左右来取比如 6 个月份对应 200这个比例在大多数屏幕上观感最均衡。barSize控制柱体粗细单位是数据空间单位。建议比 grid3D 的盒长除以分类数量的五分之一略小一些我的模拟数据里barSize: 12就是这么来的。如果 series 不止一组柱体还会横向排开此时 barSize 要相应缩水一半左右否则相邻系列会互相穿插。shading: realistic是材质渲染模式它会让柱子表面出现高光和环境反射立体感最强。可选值还有color纯色和lambert兰伯特纯漫反射。realistic 模式性能开销稍高但视觉效果全面提升大屏场景建议优先用。如果数据量极大或者设备性能差再退回到 lambert。3.3 一分钟改成 3D 散点图看清数据聚类如果数据不是柱状而是离散点比如几百个用户样本的“使用时长、消费金额、活跃度”三围数据直接把 series.type 改成 scatter3D 就行但有两个参数需要额外调。series: { type: scatter3D, symbolSize: 5, data: [ [120, 320, 0.76], [80, 520, 0.45], // 更多模拟点... ] }symbolSize此时可以是常数密度小时建议调大至 6-8密度大时调小到 2-3否则会糊成一团。散点图配visualMap非常好看颜色能从蓝渐变到红聚类关系一眼看穿。我在这套散点里还会额外加一个extra字段在 tooltip 里展示例“第 4 个维度渠道来源”鼠标悬浮就能看到多维细节这个能力是普通 canvas 散点图很难做到的。4. 进阶geo3D 地图与 globe 地球的实战4.1 地图数据的加载与坐标系选择地图类场景是 echarts-gl 里最吸引眼球的部分。geo3D 坐标系依赖 GeoJSON 数据加载方式和 ECharts 注册地图类似。业务里我们使用了脱敏处理后的模拟区域边界数据你可以把任意的边界 JSON 文件放在静态目录里用 fetch 拉取后注册。fetch(/mock/region.json) .then(res res.json()) .then(geoJson { echarts.registerMap(mockRegion, geoJson); initGeo3DChart(); });注册之后在 option 里配置 geo3D直接把区域名和数据关联。geojson 里的 properties.name 作为关联字段series 的 data 里给出每个区域名称和对应数值。geo3D: { map: mockRegion, regionHeight: 8, itemStyle: { color: #1a2b4a, opacity: 1, borderWidth: 1, borderColor: #4a7bc0 }, label: { show: true, textStyle: { color: #fff, fontSize: 10 } }, emphasis: { label: { show: false }, itemStyle: { color: #ff8c00 } }, light: { main: { intensity: 1.1, shadow: true } }, viewControl: { alpha: 40, beta: 0, distance: 120, autoRotate: false } }, series: [{ type: bar3D, coordinateSystem: geo3D, data: [ { name: A区, value: [126, 31, 500] }, { name: B区, value: [118, 38, 320] }, // 模拟区域数据 ], barSize: 1.5, shading: realistic, emphasis: { label: { show: true } } }]这里最关键的是regionHeight。它表示地图从平面拉升出来的厚度默认值是 3单位是“米”这样的场景单位。小值会让地图像纸片大值会让地图像积木块。我两版对比下来用于大屏展示的省级区块regionHeight 取 6-10 之间立体感最好100% 平面视觉和 100% 高度爆炸之间有一个甜点区间。4.2 柱状图压在地图上的两种做法在地图上叠加柱体的需求很常见但实现路径有细微差别容易踩坑。第一种是 series 类型仍是 bar3D但coordinateSystem改成geo3D。数据的 value 前三项是[经度, 纬度, 高度]系统自动把柱体“插”在地图表面上。这种写法适合表现“某个点的某个值有多大”比如各区域销售额。第二种是直接用原生的柱体线条效果用 lines3D 画“从地面到高空”的连线或飞线。它适合表现“从一个点到另一个点的关系”比如跨区域物流链路。lines3D 的核心配置是effect字段show: true之后线体会变成粒子流动效果视觉冲击力比静态线强很多。series: [{ type: lines3D, coordinateSystem: geo3D, effect: { show: true, trailWidth: 3, trailLength: 0.2 }, data: [{ coords: [ [126, 31], [118, 38] ] }] }]trailLength表示粒子拖尾的长度数值越大拖尾越长越大屏效果越好但也别超过 0.4否则看起来像通信故障的电视雪花。飞线动画的启停可以通过viewControl.autoRotate与新数据刷新配合实现这部分我在后面的封装模板里会再展开。4.3 globe 地球的配置在三维球面上表达全球数据globe 是 echarts-gl 里最“炫技”的坐标系但它的配置心智跟 geo3D 完全不同。globe 不依赖 GeoJSON 边界而是直接加载影像纹理或者高度数据数据点是经纬度坐标直接投射到球面上。globe: { globeRadius: 100, environment: #000, baseColor: #0a1a3a, shading: realistic, light: { sun: { intensity: 2, color: #fff } }, viewControl: { alpha: 30, beta: 0, distance: 180, autoRotate: true, autoRotateSpeed: 3 }, layers: [ { type: blend, blendTo: luminance, texture: textures/earth.jpg } ] }, series: [{ type: bar3D, coordinateSystem: globe, data: [ { value: [100, 40, 200] }, { value: [-30, 60, 300] } ], symbol: circle, symbolSize: 8, itemStyle: { color: #ffd500 } }]地球场景典型的坑是纹理图片加载失败球面会变成纯色看不出来是地球。所以我的做法是把纹理路径统一收在一个资源管理模块里提前做预加载和失败重试。另一个坑是 globe 上如果同时挂 bar3D 和 scatter3D多个 series 的数据堆积在球面上视觉上会互相遮挡。解决办法是控制每个系列的symbolSize并配合visualMap让数据少而精。globe 更适合“全球链路”“海外节点分布”这种故事线如果数据只集中在两三个城市用 geo3D 拉近视角比 globe 转圈更有信息密度。别为了炫技而炫技这个判断在做可视化选型时特别重要。5. 性能优化、常见报错与避坑清单5.1 数据量变大之后的三条优化路径echarts-gl 底层走 WebGL理论上渲染几千个点没有压力但真实项目里每个点还带着标签、悬浮效果、动画性能瓶颈很快会出现。我总结的三条优化路径按优先级排列如下。首先是降维数据。把三千个数据点按业务规则聚合比如把按天粒度汇总成按周把订单级明细聚合成区域级汇总。数据可视化做的是“信息可读性”不是“数据搬运工”聚合在业务上往往更有意义。其次是开启后处理时留后路。postEffect 里的 SSAO 在大屏端看起来高级但性能损耗明显如果目标机器是老旧一体机建议只开 bloom把 SSAO 和景深都关掉。最后是栅格化标签。数据点上如果 label 全开几十个标签同时渲染会导致视图旋转时卡顿可以把 label 只在emphasis状态下开启。我还试过用sampling配置来降采样grid3D 和系列层面都有类似参数。它能在保持整体形状的情况下自动抽取特征点适合折线、曲面这类连续数据对离散柱状图效果反而有限。5.2 白屏、闪烁、错位这些高频故障怎么查我整理了一个速查表全是这两个晚上实际踩过的坑。现象可能原因排查与解决页面白屏控制台无报错WebGL 上下文创建失败用 checkWebGL 检测提示用户开启硬件加速地图区域不显示GeoJSON 加载失败或地图未注册在 fetch 的 then 里 console 验证 geoJson 是否解析成功柱子“躺”在地上数据里 z 值是 null 或 0检查 series.data 第三位数值别把高度写成百分比柱体穿透地图表面经纬度顺序写反确认是 [经度, 纬度] 不是 [纬度, 经度]旋转时卡顿明显后处理开太多或点数量过大关 SSAO减少 symbolSize聚合数据tooltip 悬停没反应系列未开启 emphasis检查是否配置 emphasis 块以及 tooltip.trigger 是否为 item颜色全部灰白light 强度为 0 或缺失增加 ambient 环境光main 光源强度调到 1.0 以上自动旋转不动viewControl.autoRotate 为 false打开 autoRotate 并确认没有外部 setOption 覆盖这些报错大多不是功能缺失而是配置项互相影响导致的“黑盒问题”。我的排查方法是先从控制台看有没有报错的 key然后逐项注释 viewControl、light、postEffect 三大件快速定位是哪一类参数影响了渲染结果效率比反复刷新页面高得多。5.3 一套可复用的模板化封装思路踩完坑之后我把公共能力收敛成一个函数后续页面只需要传数据和类型就能复用。这个模板的核心是把 tooltip、light、viewControl、postEffect 全部做成默认项把 series 的差异剥离出来。export function createGLChart(dom, seriesList, extraConfig {}) { const chart echarts.init(dom); const option { tooltip: { trigger: item }, grid3D: { boxWidth: 200, boxDepth: 100, viewControl: { alpha: 25, beta: 40, distance: 220, autoRotate: true, autoRotateSpeed: 6 }, light: { main: { intensity: 1.2, shadow: true }, ambient: { intensity: 0.3 } } }, series: seriesList }; chart.setOption(Object.assign({}, option, extraConfig)); return chart; }这个封装的价值在于新页面接入时只需要维护 series 数组那些灯光、视角、后期配置不用每次复制粘贴。真正重要的是把视角参数按页面沉淀成一套配置规范主页面用 25 度俯瞰下钻页面用 40 度平视这样视觉语言统一业务方也能形成肌肉记忆。6. 进阶实践从“能显示”到“能用稳”6.1 交互与联动把领导想看的重点放在第一步3D 图表很容易让人沉迷于旋转视角但业务看板的核心是“三秒内看懂”。我给大屏设定的交互规则是默认视角固定在一个最能表达主题的角度不自动旋转鼠标悬停时高亮目标柱子点击柱子时切换到下钻页面。这个交互效果是通过 echarts 的事件机制实现的。点击 bar3D 系列时回调参数里能拿到当前柱体对应的索引和数据项。我用它去联动另一个区域的 2D 折线图这样 3D 图承担“总览定位”2D 图承担“细节补充”信息层级非常清晰。不要在一个图上塞满信息这是很多 3D 大屏项目翻车的核心原因。6.2 主题配色与“产品感”的三个细节3D 场景的配色比 2D 敏感得多。我用的主题是深蓝色背景、冷色系柱子、暖色高亮点这样符合大屏会议室里的明暗环境。再分享三个小细节。第一是环境光颜色。我试过纯白环境发现柱子暗部会发灰后来把环境光改成淡蓝色暗部立刻变得通透。第二是背景不要纯黑纯黑会让前景颜色溢出过度稍微偏深蓝的效果更耐看。第三是柱子的颜色映射尽量用视觉连续色带而不是离散色板。3D 柱状图的立体明暗已经给画面增加了对比再用跳跃的颜色会显得花哨。这些细节没有标准答案但我建议团队里先定一套规范再开做否则三个人能做出来三种配色。大屏项目最后拼的往往不是技术是统一性和一致性。6.3 大屏部署时最容易忽略的事如果你要把 echarts-gl 页面放到正式环境有三个点我建议提前处理。一个是以太网环境下的纹理资源加载。globe 的纹理、地图的 GeoJSON 都建议本地化不要依赖外部 CDN。在大屏内网环境里外部资源被拦截是常态资源失败了页面就白屏。另一个是图表容器尺寸自适应。echarts-gl 对容器尺寸变化比较敏感做窗口缩放探测时不要只监听 resize还要在容器尺寸变化后调用 chart.resize()否则会出现黑边区域。第三个是异常恢复。我用chart.getZr().on(click, ...)绑定全局错误捕获当 WebGL 上下文丢失时自动重新初始化图表并恢复最后的 option这个兜底脚本在大屏连续运行 24 小时的过程中真的帮我避免了两次全场黑屏的尴尬。7. 一段小配置实测把整套思路串起来最后用一个完整的小 Demo 把整套思路穿起来。这个例子不依赖任何真实数据和资源所有数据都写在配置里你可以直接打开页面看到效果。!DOCTYPE html html langzh-CN head meta charsetUTF-8 / titleecharts-gl 初体验示例/title style html, body, #chart { width: 100%; height: 100%; margin: 0; background: #0d1b2a; } /style /head body div idchart/div script typemodule import * as echarts from https://cdn.jsdelivr.net/npm/echarts5/dist/echarts.min.js; import https://cdn.jsdelivr.net/npm/echarts-gl2/dist/echarts-gl.min.js; const chart echarts.init(document.getElementById(chart)); const xData [一月, 二月, 三月, 四月, 五月, 六月]; const yData [产品A, 产品B, 产品C]; const data []; for (let i 0; i xData.length; i) { for (let j 0; j yData.length; j) { data.push([i, j, Math.round(200 Math.random() * 800)]); } } chart.setOption({ tooltip: {}, visualMap: { max: 1000, inRange: { color: [#313695, #4575b4, #74add1, #fee090, #f46d43] } }, xAxis3D: { type: category, data: xData }, yAxis3D: { type: category, data: yData }, zAxis3D: { type: value }, grid3D: { boxWidth: 200, boxDepth: 100, axisLabel: { textStyle: { color: #fff } }, viewControl: { alpha: 25, beta: 40, distance: 220, autoRotate: true, autoRotateSpeed: 6 }, light: { main: { intensity: 1.2, shadow: true }, ambient: { intensity: 0.3 } } }, series: [{ type: bar3D, data: data, shading: realistic, barSize: 12, emphasis: { label: { show: true, textStyle: { color: #fff, fontSize: 12 } } } }] }); /script /body /html这个 Demo 的代码风格非常接近生产项目的基础模板。你跑起来之后可以尝试调整viewControl.alpha观察从俯视到平视的变化再试着把shading从realistic改成lambert感受材质差异。这种微调的过程就是最快建立手感的方式。如果你是想快速验证的读者直接把这段 HTML 保存成文件在本地起一个静态服务打开即可不需要安装任何依赖。我对 echarts-gl 的总体看法是它不能替代数据分析和业务梳理但它是现阶段把分析结果翻译成“让人记住的视觉”的最高效工具之一。我甚至建议团队在回归测试阶段把图表跑在一个低性能环境里做一次流畅度压力测试早发现早调整好过大屏现场翻车。最后再分享一个小技巧开发时可以把viewControl.autoRotate打开让图表自动转起来再截图这样你能快速看到各个角度的效果比手动拖拽更高效。真正上线前再考虑是否关闭自动旋转按业务场景做取舍。这套流程我现在仍然在用希望你也能少踩几个坑快速把 echarts-gl 用起来。

关于本文作者

来自尧图内容编辑团队

尧图内容编辑团队 内容团队

尧图内容编辑团队

本文由尧图网络内容编辑团队执笔。团队由资深项目经理、前端工程师与设计师组成,所有内容均来自亲手交付的真实项目,先讲清问题、再给出可落地的解法。尧图深耕北京网站建设十年,服务过京华建材集团、智造科技等各行业客户,把一线经验沉淀为可复用的行业观察。

  • 十年建站经验,覆盖建材、制造、服务、文创等
  • 项目经理把关选题与事实准确性
  • 工程师与设计师联合撰写专业细节
  • 统一编辑规范,保证文风与排版一致
  • 每月复盘转化数据,迭代选题方向

延伸阅读

相关资讯与近期热门内容

深度阅读推荐

建站决策前值得细读的三篇

网站改版的5个关键决策
2024-08-12

网站改版的5个关键决策

什么时候该改版、改到什么程度、如何避免流量掉光,京华建材集团改版复盘给出答案。

获取专属建站方案

看完文章,把您的行业与预算告诉我们,免费获取一份量身定制的官网建设方案与报价。

立即免费咨询