ARTICLE DETAIL

资讯详情

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

ECharts实现3D饼图全攻略:echarts-gl与自动旋转实战

ECharts实现3D饼图全攻略:echarts-gl与自动旋转实战 做数据可视化的时候饼图算是出镜率比较高的基础图形了但静态平面饼图看久了总感觉缺了点吸引力。最近在做一个大屏展示项目时甲方明确要求一定要有3D质感、最好还能自动旋转的饼图我第一反应就是用echarts实现毕竟整个项目的数据图表都基于echarts不想为了一个饼图再引入一套重型3D框架。这里先给结论原生echarts只能画普通饼图要实现3d饼图并带3d旋转效果目前最主流也最省事的做法是把echarts和echarts-gl扩展搭配起来用通过pie3D系列加viewControl的autoRotate参数来完成效果足够惊艳实现成本也比想象中低。写这篇文章的起因挺简单我在几个技术社区里翻了翻发现很多人都在问同一个问题“有没有echarts 3d饼图示例”“echarts怎么让饼图转起来”但回复里能直接抄作业的并不多。所以我打算把这次从选型到落地的完整过程整理出来包括为什么要用echarts-gl而不是别的方案、坐标系是怎么换算的、空心环怎么做、自动旋转的参数怎么调以及我实际踩过的那些坑。不管你是刚接触echarts的新手还是已经写过不少2D图表、想往3D方向试试的老手这篇文章应该都能让你少折腾几个小时。1. 为什么ECharts自己画不出3D饼图以及三条可行技术路线1.1 看待这个问题的第一原则很多人第一次搜“echarts 3d饼图”时都会有个误解以为echarts的series里加一个什么参数就能把普通饼图“立”起来。实际上echarts核心库的饼图是基于Canvas 2D渲染的它只有平面坐标系和极坐标系的概念没有真正意义的深度轴和相机视角。所谓“3D”本质上是要让图形拥有厚度、光照、透视关系和空间旋转能力这些都是原生echarts所不具备的。所以在动手之前必须先建立一个认知你要做的不是“给饼图加个3D开关”而是“在echarts体系里引入一个能渲染3D场景的扩展”。搞清楚了这一点后面所有的方案选型和技术细节就都有了立足点。1.2 三条路线的对比与选型目前在echarts体系内做3D饼图常见的路线有三条我分别说一下它们的适用场景和取舍。第一条路线使用echarts-gl扩展库。这是百度官方维护的WebGL扩展pie3D系列天然支持饼图的三维渲染并且viewControl提供了alpha、beta、distance、autoRotate等一整套视角控制能力。它最契合“echarts实现3d饼图”这个需求因为所有配置项都延续了echarts的option语法学习成本低改动量小和已有的图表代码风格保持一致。这次我主要用的就是这条路线。第二条路线引入Three.js或Babylon.js这种独立3D引擎自行建模、打光、写动画。这种方式自由度最高能做出极其炫酷的视觉效果但代价也很大你需要额外维护一套与echarts完全无关的渲染逻辑数据联动、事件交互、tooltip等都要自己重新实现开发周期通常以天为单位。如果你只是需要一个能转的3D饼图搞这么重没必要。第三条路线用伪3D技巧“骗”过眼睛。最常见的手法是用图形叠加或阴影渐变模拟立体感比如在饼图下方叠加一圈深色扇形模拟厚度或者用多个半透明圆弧叠出层次。这种方案胜在零依赖、兼容性好但没法自由旋转视角换个角度立体感就露馅了。它适合“只要截图好看、不做交互”的场景放在大屏上静态展示还能接受要旋转就不行了。一句话总结我的选型逻辑如果你已经用echarts又想快速获得可靠的3D旋转效果echarts-gl是最优解如果你对视觉效果有极端要求不如直接上Three.js如果只是静态展示才考虑伪3D。2. 3D饼图的数学基础扇形如何“站”起来2.1 坐标系换算从极坐标到3D空间很多人在配置饼图3D时失败问题不在代码写错而是没搞懂pie3D到底是怎么把2D的扇形变成3D柱体的。我试着用一个生活化的比喻来解释普通2D饼图就像一张圆形的纸片平放在桌面上每个扇形都在纸片平面内而3D饼图相当于把这些扇形都“竖着拉高”变成一个个像切好的蛋糕块一样立在桌面上。每块蛋糕有顶面、底面和侧面顶面就是原来的扇形形状高度就是饼图的厚度。从数学角度看pie3D实际上是把每个数据项的占比转换成了角度范围然后在三维空间中把对应角度区间的扇形柱体渲染出来。这个角度换算规则和普通饼图完全一致某个数据项的值占所有数据项总值的比例乘以360度就是它占据的圆心角。比如数据项A的值是30总值为120那它的圆心角就是30除以120再乘360等于90度。理解了这一点你就能明白为什么pie3D要求每个数据项必须同时有name和value并且value不能为负数。value是用来算角度的name是用来显示标签的。如果某个数据项的value为0它就不会被渲染写了也白写。2.2 关键参数height、startAngle、minAngle 的作用配置3D饼图时有几个参数需要重点理解它们共同决定了这块“蛋糕”的形态。height参数控制饼图的厚度也就是“蛋糕块”从桌面往上长了多高。这个值要配合你图表所在坐标系的实际视觉比例来看没有绝对标准。我在实际项目里一般把图表的容器设置为600乘500左右height取20到30之间比较自然。如果height太小饼图看起来就像一张薄饼3D感很弱如果height太大整个图形又会显得臃肿旋转时会挡住其他扇区影响数据可读性。这里有个小经验先设一个初始值然后通过视角alpha的角度去微调让厚度和视觉高度达到平衡。startAngle参数决定第一个扇区从哪个角度开始分布。默认从90度开始也就是从12点钟方向顺时针排布。这里要特别注意pie3D的startAngle和普通饼图一样遵循顺时针方向如果你把startAngle改成0第一个扇区会从3点钟方向开始。如果项目里对扇区起始位置有设计要求比如想让最大扇区从正上方开始就需要计算好startAngle的偏移值。minAngle则是一个防止“视觉事故”的参数。当数据项很多、占比很小时对应扇区的角度可能只有几度在3D场景中这些细小的扇形侧边会显得特别窄甚至因为像素精度问题出现闪烁或锯齿。设置minAngle为5到8度可以强制每个扇区至少占这么多角度避免小扇区糊成一团。但也要注意这会让所有扇区的实际角度和真实占比产生偏差所以只建议在数据项大于等于6个、且存在极小占比场景时才启用。2.3 用内部比率参数做一个空心环如果你想要的是中间镂空的环形3D饼图而不是实心的大饼在echarts-gl里不需要额外构造数据直接设置internalDiameterRatio参数就行。这个东西的中文含义是“内径比例”取值在0到1之间0代表没有内孔0.5就代表内孔半径是外圆半径的一半。这个参数的设计思路很有意思你可以把整个饼图想象成一个圆环外部扇形依然按数据占比分配角度但上表面从圆形变成了圆环。渲染层会自动把每个扇区从“扇形”改写为“扇形环”。在做大屏数据展示时环形图能更好地适配中间放核心指标KPI数字的布局视觉上也比实心饼图更轻盈。这一段我单独拎出来讲是因为它不算特别起眼的属性但实际用起来对效果提升非常明显。3. 完整实现安装、引入与基础配置3.1 安装并引入echarts-gl第一步是在项目里安装echarts-gl前提是你已经装好了echarts本体。我用npm管理依赖直接在项目根目录执行npm install echarts echarts-gl如果你用的是CDN引入方式那就在页面里按顺序引入三个文件注意顺序不能乱先引入核心库再引入扩展库script srchttps://cdn.jsdelivr.net/npm/echarts5/dist/echarts.min.js/script script srchttps://cdn.jsdelivr.net/npm/echarts-gl2/dist/echarts-gl.min.js/script在Vue、React这类工程化项目中只需要在组件里引入一次。我的习惯是放到main.js里全局注册这样所有组件都能直接用3D系列。普通的按需引入写法也可以import * as echarts from echarts; import echarts-gl;这里有个容易踩的坑echarts-gl必须要在echarts之后加载如果顺序反了控制台会直接报错提示找不到echarts实例。我第一次用CDN方式做demo时就犯过这个错卡了将近二十分钟才发现是引入顺序问题。3.2 基础3D饼图配置下面给一份可以直接运行的完整配置代码我把注释写得详细一点方便你对照理解每个参数的作用。const chartDom document.getElementById(chart); const myChart echarts.init(chartDom); const option { backgroundColor: #1a1a2e, series: [{ type: pie3D, data: [ { name: 直接访问, value: 335 }, { name: 搜索引擎, value: 310 }, { name: 邮件营销, value: 234 }, { name: 联盟广告, value: 135 }, { name: 视频广告, value: 548 } ], // 饼图的厚度数值越大越立体 height: 22, // 第一个扇区的起始角度 startAngle: 90, // 极小扇区的最小角度防止糊成一团 minAngle: 3, // 内径比例0为实心饼图0.4为环形饼图 internalDiameterRatio: 0, // 标签配置 label: { show: true, formatter: {b}\n{c} ({d}%), textStyle: { color: #fff, fontSize: 14 } }, // 扇区样式 itemStyle: { // 不透明度适当调低一点会有通透感 opacity: 0.95 }, // 高亮状态 emphasis: { label: { show: true, fontSize: 18 } }, // 光照配置决定3D质感的成败 light: { main: { color: #fff, intensity: 1.1, shadow: true }, ambient: { color: #fff, intensity: 0.3 } } }] }; myChart.setOption(option);拿到这份配置后建议你先跑起来看看效果再逐个参数调整。我自己的经验是先把height调到30看立体感的极限再往回收到合适的值这样能快速建立参数和视觉效果之间的对应关系。3.3 增加底部支撑圆盘饼图立起来了但柱体和底座之间如果直接悬空视觉上会有点“飘”。社区的常见做法是用一个surface3D系列的参数化平面画一个扁平的圆盘当作地面底座这样整体看起来就有了“放在台面上”的效果。这个圆盘的实现思路并不复杂它本质上是利用parametricEquation参数化方程绘制了一个圆形平面。我参考的是社区里流传很广的写法{ type: surface3D, parametric: true, silent: true, wireframe: { show: false }, shading: color, parametricEquation: { u: { min: 0, max: Math.PI * 2, step: Math.PI / 36 }, v: { min: 0, max: 1, step: 1 }, x: function (u, v) { return Math.sin(u) * 6; }, y: function (u, v) { return 0; }, z: function (u, v) { return Math.cos(u) * 6; } }, itemStyle: { color: #2d2d45 } }这里解释一下这三个坐标函数的含义x和z是两个水平方向的分量它们用sin和cos生成了一个标准的圆形轨迹y是垂直方向的分量固定为0表示平面紧贴地面。u的取值范围0到2π正好画完整个圆v控制的是径向范围这里固定为1就表示一整块圆盘不分区。圆盘的半径6需要根据饼图的整体尺寸来调整如果饼图数据值比较大、饼面较宽就需要把圆盘半径适当调大我一般通过对比渲染效果反复试几次让圆盘刚好比饼图的半径大出一圈这样既不显得局促也不会喧宾夺主。3.4 光效、背景与质感调优3D效果好不好看很大程度上取决于光照参数而不是数据本身。很多新手第一次渲染3D饼图时觉得“颜色有点假”通常都是因为光照没调好。light.main代表主光源它决定了物体表面受光面的明暗对比。intensity表示光照强度默认是1如果希望图形更亮、色彩更鲜艳可以考虑把强度调到1.2以上。同时要注意主光源还支持alpha和beta两个方向参数它们决定了光线从哪个角度照射物体。alpha控制光线的垂直角度beta控制水平角度。我遇到过一种情况设置了shadow为true但图表的3D感还是很弱最后发现是主光源的方向几乎是从正上方垂直照下来的导致所有扇区看起来都差不多亮没有明暗过渡。后来把主光源的alpha调到30度左右明暗对比就出来了。light.ambient是环境光它负责给物体的阴影部分补充亮度。如果环境光太弱阴影会过黑如果太强又会把明暗对比“抹平”让3D感消失。我的建议是先把主光源intensity设成1.5环境光intensity设成0.2先在暗环境里观察一轮再逐步把环境光加到0.3到0.5之间。这套方法虽然不是绝对标准但至少能帮你快速定位问题出在哪类光照上。背景和整体色调也需要配套调优。纯黑背景配高饱和度的扇区颜色会显得有点“蹦迪风”深蓝灰色的背景配合低透明度的扇区反而更容易做出高级感。echarts-gl还支持给3D场景设置环境贴图通过environment参数传入一个HDR图片路径可以让物体表面产生类似反射的效果。不过环境贴图对浏览器性能要求较高而且图片资源不好找我在大多数国内项目里都选择了跳过直接靠光照调出理想效果。4. 实现3D自动旋转效果4.1 viewControl的核心属性3D饼图要“转起来”核心在于viewControl配置项。它在series层级下控制的是三维场景的相机视角。所谓“相机视角”你可以把它理解成你眼睛在3D场景中观察物体的位置和朝向alpha是俯仰角beta是水平环绕角distance是相机到物体的距离。当autoRotate设置为true时场景会持续地绕着一个中心轴自动旋转效果就是整个饼图在画面里慢慢转动。这个机制本质上就是beta角按时间递增相机的位置在圆周上移动你看到的效果就是物体转了一圈又一圈。除了自动旋转viewControl还允许用户手动拖拽旋转。这是因为3D场景默认绑定了鼠标拖拽交互用户按住鼠标左键拖动相机视角就会跟着变化。这个交互和自动旋转并不冲突手动拖拽的瞬间会打断自动旋转但松开鼠标一阵子后又会自动恢复。如果你不想要这种自动恢复可以配合事件监听来控制后面我会具体说。4.2 自动旋转的推荐参数组合下面这份配置是我在多个大屏项目里反复调过的用在大部分数据量不是特别大的饼图上都表现稳定viewControl: { // 初始俯仰角25度到35度之间看起来最舒服 alpha: 25, // 初始水平角 beta: 35, // 相机距离值越大图形越小 distance: 220, // 自动旋转开关 autoRotate: true, // 旋转速度单位是角度/秒 autoRotateSpeed: 6, // 启用阻尼效果拖拽后滑动更自然 damping: 0.8, // 旋转中心的坐标默认在图表中心 center: [0, 0, 0] }这里我想多说一下autoRotateSpeed。这个值并没有官方文档给出的“推荐区间”全凭手感。我试过从1到30之间的各种值个人经验是大屏展示需要让人看清每个数据项速度取4到8之间比较合适如果是做氛围背景速度可以适当加快但超过15之后就会因为转动太快而产生眩晕感反而不适合阅读数据。alpha和beta的初始值也很关键。alpha我这里设成25度这会让你看到饼图带一点俯视角度既有3D厚度感又不会让某些靠后的扇区被过分遮挡。如果你把alpha设成接近90度那就和从上往下看一个圆柱没有区别3D效果反而削弱了。beta的初始值决定了第一眼看到的扇区分布一般设成30到45度让图表看起来有一点斜侧感比较生动。4.3 手动控制旋转的交互方案自动旋转解决的是“无人操作时页面看起来生动”的问题但在一些特殊场景下你可能需要手动控制旋转节奏。比如我在做一个数据大屏时老板要求在某个时间点让饼图暂停旋转突出展示关键数据项过几秒再恢复。做法是在初始化图表后监听鼠标事件同时在合适时机调用chart.dispatchAction或者直接重新setOption来修改autoRotate值。举个例子我希望鼠标移入图表区域时暂停旋转鼠标移出时恢复可以这样写chartDom.addEventListener(mouseenter, function () { myChart.setOption({ series: [{ type: pie3D, viewControl: { autoRotate: false } }] }); }); chartDom.addEventListener(mouseleave, function () { myChart.setOption({ series: [{ type: pie3D, viewControl: { autoRotate: true, autoRotateSpeed: 6 } }] }); });这里注意一个细节setOption默认是merge模式你只需要把要修改的配置项传进去就好不需要重新写一整套option。但如果你多次调用setOption之后发现旋转速度越来越快那就要检查是不是每次都叠加了同一个配置。遇到这种问题先调用一下myChart.clear()再重新setOption确保状态干净。5. 常见问题与排查技巧实录5.1 饼图不显示或白屏这是3D图表最常见的故障之一。饼图完全没渲染出来但控制台也没有报错或者报错信息很模糊这种情况多半是echarts-gl没有正确加载。你先检查一下引入顺序确认echarts在echarts-gl之前加载。如果顺序没问题再看一下echarts版本和echarts-gl版本是否兼容目前比较稳妥的组合是echarts 5.x配echarts-gl 2.x。如果你用的是更高版本的echarts可能会因为接口变动导致3D系列无法正常工作。还有一种容易被忽略的情况容器div没有设置高度。2D图表在容器高度为0时只是看不到内容但3D图表的相机距离计算依赖容器尺寸高度为0时可能直接报错。记得检查一下容器尺寸是否正常。5.2 旋转时图形抖动或闪烁这个现象通常和两个因素有关。第一个因素是alpha角度过大或距离过近导致视角在旋转过程中经过饼图内部视觉上就会出现断层或穿模。解决办法是限制alpha的最大值或者把distance调大一点。第二个因素和显卡性能有关。如果机器没有开启硬件加速WebGL渲染会出现明显的帧率波动尤其在数据量较大、扇区较多时更容易出现。排查时把autoRotateSpeed先降到一个很低的值如果抖动消失了说明是渲染性能问题这时候需要在操作系统的浏览器设置里开启硬件加速或者考虑减少扇区数量、关闭shadow阴影。5.3 3D饼图被截断或超出画布饼图旋转到某些角度时画面边缘会被容器边界切掉这通常是因为相机距离太小饼图在旋转过程中超出了可视范围。最简单的办法是把distance调大一些。我在实际使用中发现饼图数据值总和比较大的时候比如各扇区value达到上千代表饼图实际渲染半径也会变大此时如果distance还在200以下很容易出现截断。建议根据饼图的实际大小动态调整distance保证饼图直径小于容器宽度的三分之二。另外还要注意viewControl里的center属性是旋转中心默认是[0,0,0]即容器正中心。如果饼图数据中设置了偏移或者pos属性可能需要同步调整center否则旋转会围绕一个不在饼图中心的位置进行视觉上像是饼图在“画圈”而不是“自转”。5.4 颜色失真、太暗或太亮这个问题基本都是光照参数导致的。颜色太暗先检查环境光intensity是不是太低了试着把ambient.intensity调到0.4以上。颜色太亮甚至发白说明主光源强度过高或者环境光太强适当降下来。如果你发现某一两个扇区颜色特别刺眼其他扇区又特别暗那大概率是主光源的角度问题改变light.main.alpha和beta的值让光照分布更均匀。还有一种情况你需要留意pie3D的itemStyle.color和普通饼图一样支持十六进制颜色但3D光照会让最终渲染出来的颜色与配置色有明暗差异。这是正常的如果你希望最终显示的颜色尽量接近配置色可以调高环境光比例比如main设0.8ambient设0.6让物体表面整体受光更均匀。5.5 自定义高亮与tooltip不生效echarts-gl的3D系列对tooltip的支持并不够完善常用的tooltip.trigger为item的配置在pie3D上并不总是生效。我经历过一次比较尴尬的场景给饼图加了tooltip结果鼠标悬停在扇区上完全没反应。后来查了一圈才搞明白pie3D的默认交互是通过鼠标点击选中高亮而不是像2D那样hover触发。如果项目一定要悬停显示提示框我的做法是自己写一个轻量级tooltip监听zr元素的mousemove事件通过myChart.convertFromPixel方法把鼠标位置转换到3D场景的坐标再找到命中的扇区然后用一个绝对定位的div显示内容。这个方法虽然麻烦一点但可控性很高而且不依赖官方tooltip的实现。如果你不想写这么复杂的逻辑也可以退一步在扇区点击事件里弹出一个自定义浮层来展示数据明细效果也说得过去。5.6 性能优化数据量大时拖拽卡顿当一个3D饼图有超过15个扇区并且开启了shadow阴影时拖拽旋转会比较吃力。这里我的可落地建议是首先关闭阴影也就是把light.main.shadow设为false这个操作能把性能提升一个档次其次调低autoRotateSpeed因为持续的高频渲染比偶尔拖拽更耗资源最后如果扇区实在太多考虑把极小占比的数据合并成一项命名为“其他”既减少扇区数量又不影响总体的比例展示。某次我做一个城市的产业占比数据原始数据有二十多个行业分类直接渲染后旋转卡顿非常明显。我先把低于2%的行业全部合并到“其他”里扇区数量减少到8个饼图旋转立刻流畅了很多。这种做法在数据可视化里其实是常见的处理思路牺牲一点数据精度换来体验的提升从视觉效果角度说是值得的。6. 一点调试经验和最后的技巧我用了不少时间研究echarts-gl的3D饼图效果坦白说它的文档并不算齐全很多好用的参数都是在社区里翻帖子翻出来的。这套方案虽然叫“echarts实现3d饼图”但核心原理已经不只是普通的2D图表绘制而是WebGL三维渲染的范畴。遇到问题时不要只盯着echarts官方文档多去echarts社区里搜“pie3D”“echarts-gl”相关的帖子很多现成案例都能给你灵感。最后再分享一个我自己一直在用的小技巧在做3D饼图配色的时候不要直接用默认的调色板颜色因为3D光照渲染后每个颜色都会产生一定程度的明暗变化。我习惯把每个扇区的颜色配上轻微的同色系渐变效果让相邻扇区即使在光线较暗的情况下也能区分开。实现上就是在数据项的itemStyle里直接写color值同时再配上opacity控制在0.85到1之间。这套风格我做了几个项目客户的统一反馈都是“这个饼图看起来比普通二维的高级很多”你下次做项目时也可以试试这个思路。
返回列表