
1. 为什么需要进度条式柱状图在日常的数据可视化工作中我们经常会遇到需要同时展示数量和比例的场景。比如一个设备管理系统需要展示各类设备的数量及其占总设备数的百分比或者一个项目管理系统需要展示各阶段的任务数量及其完成率。传统做法是用两个独立的图表来展示但这不仅占用空间还增加了用户的认知负担。进度条式柱状图通过双系列叠加和富文本标签的巧妙组合将两类信息完美融合在一个图表中。左边显示类别和具体数值中间用彩色进度条直观展示比例右边用百分比精确标注。这种设计既节省空间又提升了数据传达效率。我在实际项目中就遇到过这样的需求客户需要在有限的仪表盘空间内同时展示服务器数量和使用率。采用这种图表后不仅界面更加简洁关键指标也一目了然。下面我们就来看看如何用ECharts实现这种效果。2. 核心实现思路解析2.1 双系列叠加原理进度条式柱状图的核心在于两个柱状图系列的叠加背景系列灰色柱状图表示总量100%前景系列彩色柱状图表示实际比例通过设置barGap: -100%让两个系列完全重叠再通过zlevel控制叠放顺序。这种技巧在ECharts中被称为系列叠加是制作复合型图表的基础手法。2.2 富文本标签的妙用传统柱状图的标签只能显示简单文本而我们需要在三个位置显示不同信息左侧类别名称具体数值中间彩色进度条右侧百分比数值这就要用到ECharts的富文本标签功能。通过在axisLabel和label的formatter中使用特殊语法我们可以为不同文本设置不同的颜色、大小和对齐方式。3. 完整实现步骤3.1 基础HTML结构首先准备一个容器建议设置固定宽高以确保图表在不同设备上显示一致div classprogress-bar-chart refchartContainer/div.progress-bar-chart { width: 800px; /* 适当宽度确保标签不拥挤 */ height: 400px; /* 高度根据数据项数量调整 */ }3.2 关键配置项详解下面是完整的配置代码我添加了详细注释说明每个参数的作用function initProgressBarChart(data) { const chart echarts.init(this.$refs.chartContainer); const option { grid: { left: 25%, // 为左侧标签留足空间 right: 15%, // 为右侧百分比留空间 top: 10%, bottom: 10% }, xAxis: { type: value, show: false // 隐藏x轴 }, yAxis: { type: category, data: data.categories, axisTick: { show: false }, axisLine: { show: false }, axisLabel: { color: #333, fontSize: 14, formatter: (name) { // 查找对应的数值 const index data.categories.indexOf(name); const value data.values[index]; // 富文本格式类别{换行}数值单位 return [ {category| name }, {value| value 台} ].join(\n); }, rich: { category: { fontSize: 14 }, value: { color: #3C83FF, fontWeight: bold, padding: [0, 0, 0, 10] } } } }, series: [ { // 背景系列灰色 name: 总量, type: bar, barWidth: 20, barGap: -100%, // 关键参数使两个系列重叠 data: data.categories.map(() 100), // 全部设为100% itemStyle: { color: #eee, borderRadius: [0, 10, 10, 0] // 只设置右侧圆角 }, label: { show: true, position: right, formatter: ({ name }) { const index data.categories.indexOf(name); return data.percentages[index] %; }, color: #666, fontSize: 14, offset: [10, 0] // 向右偏移避免紧贴柱子 } }, { // 前景系列彩色 name: 进度, type: bar, barWidth: 20, data: data.percentages, itemStyle: { color: new echarts.graphic.LinearGradient(0, 0, 1, 0, [ { offset: 0, color: #4facfe }, { offset: 1, color: #00f2fe } ]), borderRadius: [0, 10, 10, 0] }, zlevel: 2 // 确保彩色柱子在上层 } ] }; chart.setOption(option); window.addEventListener(resize, chart.resize); }3.3 数据处理技巧传入的数据应该包含三类信息const chartData { categories: [设备A, 设备B, 设备C], // 类别名称 values: [45, 78, 23], // 具体数值 percentages: [30, 52, 15] // 百分比0-100 };实际项目中百分比通常需要计算得到function processData(rawData) { const total rawData.reduce((sum, item) sum item.value, 0); return { categories: rawData.map(item item.name), values: rawData.map(item item.value), percentages: rawData.map(item Math.round((item.value / total) * 100)) }; }4. 高级优化技巧4.1 响应式布局处理为了让图表在不同屏幕尺寸下都能良好显示需要做两件事容器自适应window.addEventListener(resize, () { if (this.chart) { this.chart.resize(); } });动态调整配置function getDynamicOptions() { const isMobile window.innerWidth 768; return { grid: { left: isMobile ? 35% : 25%, right: isMobile ? 5% : 15% }, yAxis: { axisLabel: { fontSize: isMobile ? 12 : 14 } } }; }4.2 动画效果增强通过设置系列动画让图表更生动series: [{ // ... animationDuration: 1500, animationEasing: elasticOut, animationDelay: function(idx) { return idx * 200; } }]4.3 交互功能扩展添加鼠标悬停效果提升用户体验tooltip: { trigger: item, formatter: ({ name, value }) { const index data.categories.indexOf(name); return div styletext-align:left b${name}/bbr/ 数量${data.values[index]}台br/ 占比${value}% /div ; } }, emphasis: { itemStyle: { shadowBlur: 10, shadowColor: rgba(0,0,0,0.3) } }5. 常见问题解决方案5.1 标签显示不全怎么办当类别名称较长时左侧标签可能被截断。解决方法调整grid的left值grid: { left: 30% // 根据实际情况增加 }使用省略号axisLabel: { formatter: (name) { return name.length 6 ? name.slice(0,6)... : name; } }5.2 数据更新时的平滑过渡使用ECharts的setOption方法第二个参数实现平滑更新// 保留之前的状态如缩放、选中等 chart.setOption(newOption, { notMerge: false, lazyUpdate: true });5.3 性能优化建议当数据量很大时超过50条建议使用large: true开启大数据模式降低动画复杂度animation: { duration: 1000, easing: linear }6. 实际应用案例6.1 项目进度管理将任务阶段作为类别已完成任务数作为数值完成率作为百分比const projectData { categories: [需求分析, UI设计, 前端开发, 后端开发, 测试验收], values: [15, 12, 8, 5, 2], percentages: [75, 60, 40, 25, 10] };6.2 资源监控看板展示服务器CPU、内存等资源的使用情况const serverData { categories: [Web服务器, 数据库, 缓存, 文件存储], values: [32, 64, 16, 8], percentages: [80, 65, 40, 20] };6.3 销售业绩展示对比各销售团队的完成情况const salesData { categories: [华北区, 华东区, 华南区, 西部区], values: [450, 680, 520, 310], percentages: [90, 68, 52, 31] // 相对于目标500万 };7. 样式自定义指南7.1 颜色主题配置使用ECharts的调色板功能统一风格const colorPalette [#3C83FF, #6CD9B3, #FFB74D, #FF8A65]; option.color colorPalette;7.2 圆角与阴影效果增强柱子的立体感itemStyle: { borderRadius: [0, 10, 10, 0], shadowColor: rgba(0,0,0,0.2), shadowBlur: 5, shadowOffsetY: 2 }7.3 字体与间距调整精细控制标签样式textStyle: { fontFamily: Arial, sans-serif, fontSize: 14, lineHeight: 20 }, label: { padding: [5, 10], backgroundColor: #f5f5f5, borderRadius: 4 }8. 与其他图表的组合使用8.1 配合饼图使用左侧用进度条柱状图展示详细数据右侧用饼图展示整体分布grid: [ { left: 55% }, // 饼图区域 { right: 55% } // 柱状图区域 ]8.2 内嵌折线图标记在柱状图上叠加折线图标记平均值series: [ // ...柱状图系列... { type: line, data: [average, average, average], markLine: { silent: true, symbol: none, lineStyle: { color: #FF6B6B, type: dashed }, label: { show: false } } } ]8.3 多维度对比使用堆叠柱状图展示更多维度series: [ { stack: total, data: [/*Q1数据*/] }, { stack: total, data: [/*Q2数据*/] }, { type: bar, data: [/*目标线*/] } ]