
ECharts 打造 2.5D/伪3D 立体柱状图 — 从零到一实现前言在大屏数据可视化项目中平面的柱状图往往显得不够有质感。很多大屏需求方会要求柱子具有立体感但又不想引入 Three.js 等重量级 3D 方案。本文将从零开始一步步拆解如何使用 ECharts 原生能力打造出带菱形顶面/底面、渐变侧面、标签图片以及背景贴图的伪 3D2.5D柱状图。最终效果柱子有顶面菱形底面多层菱形光晕侧面双向渐变标签带背景图柱子背后有纹理图片整个图表看起来像是三维渲染但本质是 ECharts 的pictorialBarbar组合。1. 起点ECharts 官方模板一切从最简单的柱状图开始。先初始化一个最基础的 ECharts 实例option { title: { text: 矩形柱状图 }, xAxis: { type: category, data: [mon, tue, wed, thu, fri, sat, sun] }, yAxis: { type: value }, series: [ { type: bar, data: [120, 200, 150, 80, 75, 105, 130] } ] };效果就是一个非常标准的平面柱状图。我们的目标是在这个基础上逐步给它穿衣服让它看起来像是三维的。2. 顶面菱形用 pictorialBar 的 diamond 给柱子加盖2.1 核心思路pictorialBar象形柱图是 ECharts 提供的一种特殊柱状图类型它可以用SVG 符号如圆形、矩形、菱形等来替代传统矩形柱子。最关键的是它有一个配置项symbolPosition: end可以让符号刚好出现在柱子顶端。我们利用这个特性在原有的bar系列之上叠加一个pictorialBar将symbol设为diamond菱形并通过symbolOffset微调位置让菱形看起来像是柱子的顶面。2.2 代码{ type: pictorialBar, symbol: diamond, // 使用菱形符号 symbolOffset: [0, -6], // 向上偏移6像素对齐柱子顶部 symbolSize: [30, 11], // 宽度30、高度11 symbolPosition: end, // 放在数据值顶端 z: 10, // 层级高于柱子确保不被遮挡 data: [120, 200, 150, 80, 75, 105, 130], itemStyle: { color: rgb(34, 114, 199) // 与柱子同色系 } }2.3 关键参数说明参数作用symbol: diamond指定符号形状为菱形symbolPosition: end符号跟随数据值出现在柱子顶部symbolOffset: [0, -6]水平0、垂直-6向上微调使菱形刚好盖在柱子顶上symbolSize: [30, 11]菱形的宽度和高度z: 10层级设置高于 bar 系列防止被压住此时运行你会发现每个柱子顶部多了一个蓝色小菱形柱子瞬间有了顶面的感觉。如果需要指定图片则需要修改symbol的配置项{ type: pictorialBar, symbol: image://https://echarts.apache.org/zh/asset/lottie/json/images/img_3.png, symbolOffset: [0, -6], // 向上偏移6像素对齐柱子顶部 symbolSize: [30, 11], // 宽度30、高度11 symbolPosition: end, // 放在数据值顶端 z: 10, // 层级高于柱子确保不被遮挡 data: [120, 200, 150, 80, 75, 105, 130], }那么你将得到一个自定义图片的顶部3. 底面菱形复制顶面思路给柱子加底3.1 核心思路有了顶面自然而然想到——能不能给柱子底部也加个菱形答案是可以的。将symbolPosition改为start菱形就会出现在柱子底部即坐标轴起点处。同样用symbolOffset微调位置。3.2 代码{ type: pictorialBar, symbol: diamond, symbolOffset: [0, -2], // 微调让菱形贴合柱子底部 symbolSize: [30, 11], symbolPosition: start, // 放在底部 z: 10, data: [120, 200, 150, 80, 75, 105, 130], itemStyle: { color: rgba(34, 114, 199, 0.3) // 半透明做出底部暗面效果 } }3.3 效果现在柱子有了顶和底立体感初步显现。底面用半透明色模拟了暗面的感觉。4. 底面光晕多层菱形叠加制造扩散效果4.1 核心思路光有一个底面菱形还不够炫。大屏项目里经常能看到柱子底部有渐变扩散的光晕效果。实现方式很简单再叠加两层菱形尺寸逐层放大透明度逐层降低。这样在视觉上就形成了光晕扩散的效果。 根据需求进行调整即可4.2 代码// 第2层底面菱形中等大小 { type: pictorialBar, symbol: diamond, symbolOffset: [0, 2], symbolSize: [50, 17], // 比第一层大 symbolPosition: start, z: 10, data: [120, 200, 150, 80, 75, 105, 130], itemStyle: { color: rgba(34, 114, 199, 0.2) // 更透明 } }, // 第3层底面菱形最大 { type: pictorialBar, symbol: diamond, symbolOffset: [0, 8], symbolSize: [70, 25], // 最大的一层 symbolPosition: start, z: 10, data: [120, 200, 150, 80, 75, 105, 130], itemStyle: { color: rgba(34, 114, 199, 0.1) // 非常淡 } }4.3 效果说明三层菱形从内到外分别是 30×11透明度0.3、50×17透明度0.2、70×25透明度0.1叠加后形成从中心向外扩散的光晕效果。这种低代码高效果的技巧在大屏开发中非常实用。5. 柱子侧面两个 bar 叠加 不同方向渐变5.1 核心思路原本的柱状图显然没有办法做到立体的效果要模拟三维柱子的亮面和暗面不能只用一个柱子。我们用两个完全重叠的 bar分别设置不同方向的线性渐变模拟光线从一侧打过来的效果。亮面右侧/正面用较亮的渐变色蓝→白模拟受光面暗面左侧/背面用较暗的渐变色深蓝→浅蓝→白模拟背光面通过barGap: 0让两个柱子完全重叠。5.2 代码替换原有bar)// 亮面柱子右侧 { type: bar, z: 1, barWidth: 15, itemStyle: { color: { type: linear, x: 0, y: 0, x2: 1, y2: 1, // 对角线方向渐变 colorStops: [ { offset: 0, color: rgb(80, 150, 255) }, // 浅蓝 { offset: 0.9, color: rgb(250, 255, 255) }, // 接近白色 { offset: 0.95, color: rgb(255, 255, 255) } // 纯白 ] } }, data: [120, 200, 150, 80, 75, 105, 130] }, // 暗面柱子左侧 { type: bar, z: 1, barWidth: 15, barGap: 0, // 与上一个柱子完全重叠 itemStyle: { color: { type: linear, x: 0, y: 0, x2: 1, y2: 1, colorStops: [ { offset: 0, color: rgb(30, 100, 188) }, // 深蓝 { offset: 0.5, color: rgb(150, 180, 255) }, // 中间亮蓝 { offset: 0.95,color: rgb(255, 255, 255) } // 白 ] } }, data: [120, 200, 150, 80, 75, 105, 130] }5.3 渐变方向说明参数含义x: 0, y: 0渐变起点左上角x2: 1, y2: 1渐变终点右下角colorStops渐变色阶数组offset范围 0~1两个柱子的渐变色阶不同亮面柱子从浅蓝渐变到白暗面柱子从深蓝渐变到白叠加后产生了明显的光影对比。具体渐变情况和渐变速度可以通过colorStops节点的设置调整。到这里就完成了伪3d或者说2.5d柱状图了。6. 标签优化从纯色背景到图片背景6.1 基础标签蓝底白字ECharts 的label配置非常灵活。先实现最基础的蓝底白字标签写在顶部pictorialBar下面label: { show: true, position: top, color: #ffffff, fontWeight: bold, fontSize: 12, padding: [6, 8], backgroundColor: #1890ff, borderRadius: 4 }6.2 进阶标签使用图片背景蓝底白字太普通我们可以用rich富文本 backgroundColor.image实现图片背景标签label: { show: true, position: top, color: #ffffff, // 白字 fontWeight: bold, fontSize: 12, // 字体大小 formatter: {bg|{c}}, // {c} 是数据值{bg|} 引用下方 rich 中的 bg 样式 rich: { bg: { backgroundColor: { image: https://echarts.apache.org/zh/asset/lottie/json/images/img_0.png }, width: 40, height: 15, padding: [10, 0, 0, 0], align: center } } }6.3 关键点formatter: {bg|{c}}{c}代表当前数据值{bg|...}表示使用rich.bg中定义的样式backgroundColor.image直接填入图片 URLECharts 会将其作为背景渲染width/height/padding控制标签块的尺寸和内边距注意注释掉的backgroundColor: #1890ff是之前的纯色方案保留在这里是为了对比两种方式的区别。7. 柱子背景纹理第二 Y 轴 pictorialBar 贴图7.1 需求背景有些大屏设计稿要求柱子内部有纹理/图案比如木纹、金属质感、科技线条而不是纯色填充。我们可以用一个巧妙的方式实现在柱子背后叠加一张图片。7.2 技术方案新增第二个 Y 轴yAxisIndex: 1设置固定最大值如 250隐藏轴线和网格新增一个pictorialBar系列symbol使用图片 URLimage://...绑定到第二 Y 轴将图片柱子的宽度设为96%略窄于真实柱子避免穿帮设置barGap: -100%让图片柱与数据柱完全重叠设置z: -1将图片柱置于最底层7.3 代码第二步 Y 轴配置yAxis: [ { type: value }, { type: value, max: 1, // 固定最大值背景图会顶到这个高度 show: false, // 隐藏这个Y轴 splitLine: { show: false // 隐藏网格线 } } ]图片背景柱子系列{ type: pictorialBar, symbol: image://https://echarts.apache.org/zh/asset/lottie/json/images/img_3.png, symbolPosition: start, yAxisIndex: 1, // 绑定到第二个Y轴 symbolSize: [96%, 100%], // 宽度96%略窄高度100%跟随数据 barGap: -100%, // 与柱子完全重叠 z: -1, // 置于最底层 data: [1, 1, 1, 1, 1, 1, 1] // 固定最大值填满整个高度 }8. 完整代码与总结8.1 完整代码option { title: { text: 矩形柱状图 }, xAxis: { type: category, data: [mon, tue, wed, thu, fri, sat, sun] }, yAxis: [ { type: value }, { type: value, max: 250, // 固定最大值背景图会顶到这个高度 show: false, // 隐藏这个Y轴 splitLine: { show: false // 隐藏网格线 } } ], series: [ // 1. 顶面菱形 { type: pictorialBar, symbol: diamond, symbolOffset: [0, -6], symbolSize: [30, 11], symbolPosition: end, z: 10, data: [120, 200, 150, 80, 75, 105, 130], itemStyle: { color: rgb(34, 114, 199) }, label: { show: true, position: top, color: #ffffff, fontWeight: bold, fontSize: 12, formatter: {bg|{c}}, rich: { bg: { backgroundColor: { image: https://echarts.apache.org/zh/asset/lottie/json/images/img_0.png }, width: 40, height: 15, padding: [10, 0, 0, 0], align: center } } } }, // 2. 柱子背景纹理图片 { type: pictorialBar, symbol: image://https://echarts.apache.org/zh/asset/lottie/json/images/img_3.png, symbolPosition: start, yAxisIndex: 1, symbolSize: [96%, 100%], barGap: -100%, z: -1, data: [250, 250, 250, 250, 250, 250, 250] }, // 3. 底面菱形第1层最小 { type: pictorialBar, symbol: diamond, symbolOffset: [0, -2], symbolSize: [30, 11], symbolPosition: start, z: 10, data: [120, 200, 150, 80, 75, 105, 130], itemStyle: { color: rgba(34, 114, 199, 0.3) } }, // 4. 底面菱形第2层中等 { type: pictorialBar, symbol: diamond, symbolOffset: [0, 2], symbolSize: [50, 17], symbolPosition: start, z: 10, data: [120, 200, 150, 80, 75, 105, 130], itemStyle: { color: rgba(34, 114, 199, 0.2) } }, // 5. 底面菱形第3层最大 { type: pictorialBar, symbol: diamond, symbolOffset: [0, 8], symbolSize: [70, 25], symbolPosition: start, z: 10, data: [120, 200, 150, 80, 75, 105, 130], itemStyle: { color: rgba(34, 114, 199, 0.1) } }, // 6. 柱子亮面 { type: bar, z: 1, barWidth: 15, itemStyle: { color: { type: linear, x: 0, y: 0, x2: 1, y2: 1, colorStops: [ { offset: 0, color: rgb(80, 150, 255) }, { offset: 0.9, color: rgb(250, 255, 255) }, { offset: 0.95, color: rgb(255, 255, 255) } ] } }, data: [120, 200, 150, 80, 75, 105, 130] }, // 7. 柱子暗面 { type: bar, z: 1, barWidth: 15, barGap: 0, itemStyle: { color: { type: linear, x: 0, y: 0, x2: 1, y2: 1, colorStops: [ { offset: 0, color: rgb(30, 100, 188) }, { offset: 0.5, color: rgb(150, 180, 255) }, { offset: 0.95,color: rgb(255, 255, 255) } ] } }, data: [120, 200, 150, 80, 75, 105, 130] } ] };如果觉得底部因为背景图片有点穿帮可以通过调整底部symbolOffset的第二个参数调整上下位置来实现适配。8.2 架构总结层级系列类型作用z: 10pictorialBar(diamond, end)顶面菱形 数据标签z: 10pictorialBar(diamond, start) ×3底面三层菱形光晕z: 1bar×2柱子侧面亮面 暗面z: -1pictorialBar(image)柱子背景纹理贴图8.3 关键技术点回顾pictorialBarsymbol: diamond用菱形符号模拟三维顶面和底面symbolPosition: end/start控制符号出现在柱子顶部还是底部多层菱形叠加通过不同尺寸和透明度实现光晕扩散效果双 bar 重叠 线性渐变用两个不同渐变方向的柱子模拟光影rich富文本标签实现图片背景的数值标签第二 Y 轴 pictorialBar图片在柱子内部叠加纹理/图案z层级控制精确控制各系列的前后遮挡关系写在最后这套方案的优点在于零依赖、纯 ECharts不需要引入任何额外库就能实现令人满意的 2.5D 效果。相比真正的 Three.js 3D 方案它在性能、兼容性和开发成本上都有明显优势非常适合大屏数据可视化场景。在渲染速度上对比于3d渲染也会快一些。读者可以根据自己的设计稿灵活调整菱形的symbolSize和symbolOffset渐变的colorStops光晕的层数和透明度标签的背景图片 URL柱子的背景纹理图片