做可视化大屏的人,多多少少都被 echarts 自定义柱状图和 legend 颜色这件事折磨过。图例颜色和柱子对不上、默认方块太丑、想换成圆点或者箭头又不知道去哪改,这些问题几乎每个项目都会遇到。这篇文章就把柱状图这块从颜色到图例形状的定制逻辑一次说清楚。内容以 echarts 柱状图为主线,把 legend 颜色来源、颜色联动关系、形状自定义的几种方案串起来,适合正在做数据可视化大屏、图表组件封装,或者单纯被 legend 默认样式逼疯的前端同学参考。
1. 柱状图颜色控制,从这三层配置说起
1.1 全局色板:color 数组怎么用才不翻车
先看最基础的问题:柱子颜色到底由谁决定。
ECharts 里有一个全局color配置项,它是一个颜色数组。当你没有给任何 series 单独指定颜色时,ECharts 会按照 series 的声明顺序,依次从color数组里取色。如果 series 数量超过了颜色数组的长度,就从头开始循环取。
option = { color: ['#3A7BFD', '#00E4FF', '#FFB938'], series: [ { type: 'bar', data: [120, 200, 150] }, { type: 'bar', data: [80, 160, 220] } ] };这段配置里,第一个柱状图系列拿到#3A7BFD,第二个系列拿到#00E4FF。
全局color的好处是省事,适合系列多、颜色只需要一套固定品牌色的场景。但它的弊端也很明显:一旦需要单独调整某个系列的颜色,你就得去 series 里覆盖;一旦系列顺序调整,颜色就可能全部错位。所以我在实际项目里更推荐显式配置,也就是下面要说的 series 级别和 data 级别。
还有一种常见用法是直接修改color数组本身来控制所有图表的默认配色,比如:
echarts.registerTheme('myTheme', { color: ['#3A7BFD', '#00E4FF', '#FF6B6B', '#7B61FF'] });初始化图表时用echarts.init(dom, 'myTheme'),这样整个项目的柱子、折线、饼图都会遵循同一套配色。对于大屏项目来说,把主题色收敛到一处,后续改需求能省非常多时间。
1.2 按数据项着色:阈值高亮与渐变柱子
实际业务里,柱状图很少是清一色同一个颜色的。最常见的需求有两个:一是让某根柱子超过阈值后变成警示色,二是让所有柱子走渐变。
先说按数据项着色。ECharts 的数据项data里可以单独配itemStyle,优先级高于 series 级别的itemStyle:
option = { series: [{ type: 'bar', data: [ { value: 120, itemStyle: { color: '#3A7BFD' } }, { value: 200, itemStyle: { color: '#3A7BFD' } }, { value: 180, itemStyle: { color: '#F56C6C' } } ] }] };如果原始数据来自接口,可以用map批量处理:
const rawData = [120, 200, 180, 90, 260]; const threshold = 150; const data = rawData.map(v => ({ value: v, itemStyle: v > threshold ? { color: '#F56C6C' } : { color: '#3A7BFD' } }));这里要注意一个很容易误导新手的点:这种数据项级别的颜色只会改柱子的颜色,不会影响 legend 图例项的颜色。legend 取的是 series 整体颜色,不是某根柱子的颜色。这个联动关系我在第二章详细展开。
再聊聊渐变。ECharts 5 里给柱子加渐变有两种写法。一种是传统的echarts.graphic.LinearGradient:
itemStyle: { color: new echarts.graphic.LinearGradient(0, 0, 0, 1, [ { offset: 0, color: '#3A7BFD' }, { offset: 1, color: 'rgba(58, 123, 253, 0.2)' } ]) }四个参数分别是渐变起点 x、起点 y、终点 x、终点 y,取值范围是 0 到 1。(0, 0, 0, 1)表示从上到下的垂直渐变,(0, 0, 1, 0)表示从左到右,(0, 0, 1, 1)是从左上角到右下角的对角渐变。
另一种是 ECharts 5 推荐的纯对象写法,不依赖echarts.graphic,更利于配置序列化:
itemStyle: { color: { type: 'linear', x: 0, y: 0, x2: 0, y2: 1, colorStops: [ { offset: 0, color: '#3A7BFD' }, { offset: 1, color: 'rgba(58, 123, 253, 0.2)' } ] } }两种效果一样,看团队习惯。我自己的经验是:如果项目里有用 JSON 动态生成 option 的场景,推荐用对象写法;如果是写死在代码里的配置,两种都可以。
1.3 渐变柱子的连带反应:legend 图例的视觉问题
柱子走了渐变之后,一个隐藏问题就浮出来了:legend 的图例色块也会变成渐变色。
因为 legend 的图标颜色默认就是从 series 的itemStyle.color取的,你给的是LinearGradient对象,图例渲染的时候就把这个渐变对象应用到了一个很小的图标区域里。柱子大,渐变看得清;图例图标那么小,渐变基本上就是糊成一团,视觉体验很糟糕。
解决办法不是去改 legend 的什么隐藏属性,而是换思路:让 legend 用 formatter 富文本自己画颜色,或者干脆不用原生 legend,自己写 HTML 图例。这两个方案我会在第三章和第四章分别讲,这里先留个印象。
2. legend 颜色到底跟谁走,怎么让它听话
2.1 legend 没有独立 color 属性,它取的是 series 颜色
这是整个问题里的核心认知,值得单独拿出来说:ECharts 的 legend 没有独立的 color 配置。
很多人第一反应是去 legend 配置里找一个类似itemStyle.color的字段,但翻遍官方文档你会发现,legend.data 里的每个图例项只能配置name、icon、textStyle、tooltip,没有 color。图例的色块颜色,是渲染时从对应 series 的数据样式里取出来的。
具体来说:
- 柱状图的图例颜色,来自该 series 的
itemStyle.color; - 折线图的图例颜色,来自该 series 的
lineStyle.color; - 如果 series 自己没设置,就取全局
color数组分配到的颜色。
所以想让图例变成某个颜色,最直接的手段是修改对应 series 的颜色。这是合法的、也是官方支持的做法。
但这引发了一个现实问题:我只想改图例颜色,不想动柱子颜色,怎么办?比如柱子要走渐变,图例想显示一个干净的纯色圆点。这种需求用原生 legend 就有点拧巴了,需要借助 formatter 来“伪装”。
2.2 三招控制 legend 颜色
第一招最省事:直接改 series 颜色,让 legend 跟着变。适合柱子颜色和 legend 颜色一致的情况,比如统一用品牌色。
第二招:用 formatter 富文本覆盖图例的视觉呈现。
原理是先让 legend 不渲染默认图标,然后在 formatter 回调里返回一段包含富文本占位符的字符串,利用富文本的 rich 样式画一个自定义色块或者带颜色的符号。
legend: { icon: 'none', formatter: (name) => { const colorMap = { '实际销量': '#3A7BFD', '目标销量': '#FFB938' }; return `{dot|●} {txt|${name}}`; }, textStyle: { rich: { dot: { color: '#3A7BFD', fontSize: 14, padding: [0, 6, 0, 0] }, txt: { color: '#D3E1FF', fontSize: 13 } } } }这段代码里有几个关键点。icon: 'none'让默认小方块不绘制,否则默认图标和 formatter 里的符号会叠加出现。富文本占位符{dot|●}的含义是:以 rich 中名为dot的样式,渲染●这个字符。●是一个 Unicode 实心圆,你还可以换成■、▲、◆等符号,形状也就跟着变了。
需要注意,不同图例项需要不同颜色时,富文本样式名不能用同一个。上面这个写法里,所有图例项都用dot这个样式,颜色写死成同一个,两个系列会一样。解决办法是让 formatter 返回不同的占位符名:
formatter: (name) => { const idx = seriesConfig.findIndex(s => s.name === name); return `{dot${idx}|●} {txt|${name}}`; }, textStyle: { rich: { dot0: { color: '#3A7BFD', fontSize: 14, padding: [0, 6, 0, 0] }, dot1: { color: '#FFB938', fontSize: 14, padding: [0, 6, 0, 0] }, txt: { color: '#D3E1FF', fontSize: 13 } } }这个方法可以把图例完全做成你想要的样子,而且点击图例控制系列显隐的交互依然有效。
第三招:放弃原生 legend,用自定义 HTML 图例。这个方案自由度最高,我把完整实现放到第四章专门讲。
2.3 多系列柱状图的图例匹配规则
当一个图表里同时有多个柱状图系列时,legend 的匹配逻辑是按name来关联的。ECharts 默认会自动生成图例项,但如果手动配置了legend.data,就必须保证每一项的name和对应 series 的name完全一致,包括空格和大小写。
series: [ { name: '实际销量', type: 'bar', data: [...] }, { name: '目标销量', type: 'bar', data: [...] } ] legend: { data: [ { name: '实际销量', icon: 'circle' }, { name: '目标销量', icon: 'circle' } ] }这里有个很容易踩的坑:如果legend.data里只写了一个项,另一个系列虽然存在,图例也不会显示;如果写了不存在的 name,图例会多出一个小项,点击时候事件也找不到对应 series,交互就出 bug 了。
还有一个实际经验:多系列柱子经常需要“图例顺序和柱子显示顺序一致”,但图例顺序并不决定 series 的绘制层级。绘制层级是由 series 数组顺序决定的,后者声明的系列会盖在前面系列的上方。如果你发现某个系列的柱子被挡住了,去调整 series 数组顺序,而不是调 legend.data 顺序。
如果你希望图例纯展示、不让用户点击隐藏系列,可以把selectedMode设为false:
legend: { selectedMode: false }这样图例不能交互,系列始终全部显示,适合大屏展示这类不允许用户随意操作的场景。
3. 自定义 legend 形状的四种方案
3.1 内置 icon 形状够用了吗
ECharts 的 legend 图标支持几种内置形状:circle(圆)、rect(矩形)、roundRect(圆角矩形)、triangle(三角)、diamond(菱形)、pin(水滴/图钉)、arrow(箭头)、none(不显示)。
| icon 值 | 形状 | 适用场景 |
|---|---|---|
| circle | 圆形 | 大屏圆点图例,最常用 |
| rect | 矩形 | 默认样式,柱状图标配 |
| roundRect | 圆角矩形 | 想要柔和一点的柱状图形状 |
| triangle | 三角形 | 特殊装饰风格 |
| diamond | 菱形 | 折线图或雷达图风格 |
| pin | 水滴/图钉 | 地图类图例常用 |
| arrow | 箭头 | 方向性数据 |
| none | 不渲染图标 | 配合 formatter 使用 |
全局统一设置:
legend: { icon: 'circle', itemWidth: 10, itemHeight: 10 }itemWidth和itemHeight控制图例图标的尺寸,注意不同形状在不同尺寸下视觉表现差别很大。比如圆点 10px 看着正好,矩形如果设成 10x10 会显得有点小,我一般给矩形设 14x8,更协调。
也可以针对每个图例单独指定形状,用legend.data数组:
legend: { data: [ { name: '实际销量', icon: 'circle' }, { name: '目标销量', icon: 'diamond' } ] }这种“不同系列不同形状”的做法在混合图表里很常见,比如柱状图配折线图时,柱状图用方块、折线图用圆点,视觉上就能直接区分。
3.2 path 路径:想要什么图标都能画
内置形状满足不了需求时,ECharts 支持用 SVG path 自定义图标。
先看一个最简单的自定义:
legend: { data: [{ name: '实际销量', icon: 'path://M0,0 L1024,0 L1024,1024 L0,1024 Z' }] }path://后面跟的是 SVG 路径字符串,坐标系范围一般建议控制在 0 到 1024 之间。上面这个路径画的是从原点出发,到 (1024,0),再到 (1024,1024),再到 (0,1024),最后闭合,实际上就是一个正方形。
如果是从设计师那边拿到的 SVG 图标,打开 SVG 源码,把<path>标签的d属性值复制出来,拼上path://前缀就行。比如一个简单的闪电图标:
icon: 'path://M 512 1024 C 256 640 160 512 160 352 C 160 157.44 317.44 0 512 0 C 706.56 0 864 157.44 864 352 C 864 512 768 640 512 1024 Z'这段路径画出来的其实是一滴水的形状,常用于地图上的位置标记。
使用 path 自定义图例有几点要注意。第一,路径的边界不一定是 0-1024,如果图形显示偏移或者被裁剪,多半是路径坐标范围不对,需要用矢量软件把图标整体挪到合适位置。第二,icon的大小仍然受itemWidth和itemHeight控制,但 path 内部的宽高比不会自动缩放为正方形,需要自己在路径数据里控制好。第三,path 图标的填充色依然来自 series 的颜色配置,所以它表现出来的是“形状自定义 + 颜色跟随系列”的效果。
3.3 图片和 base64:最省事的兜底方案
不想折腾 path 的话,image://方案最直接。
legend: { data: [{ name: '实际销量', icon: 'image://https://example.com/icon.png' }] }也可以直接把图片转成 base64 字符串塞进去:
icon: 'image://data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAA...'这里有个容易出问题的细节:image://后面的 URL 如果有特殊字符,比如某些 CDN 地址带了签名参数,可能需要做 URL 编码,否则加载失败。而且开发环境和生产环境的图片路径往往不一致,建议封装一个公共方法统一拼接图片前缀。
图片方案的坑主要在两个方面。一是图片尺寸和itemWidth、itemHeight的配合,图片不会被裁剪,但会等比缩放,如果源图标比例和 item 尺寸比例差别太大,图例看起来会有留白或者被压缩的感觉。二是跨域图片在部分浏览器下可能无法绘制到 canvas 上,导致图例图标空白,这种情况一般切换成 base64 或者用本地图片资源能解决。
3.4 formatter 富文本:用文字符号伪装图例形状
这是我在大屏项目里用得最多的方案,因为它不用切图、不用调 path 坐标,灵活性最好。
核心思路是利用legend.formatter返回富文本字符串,在字符串里用{样式名|内容}的语法插入一个任意字符当作图形。
legend: { icon: 'none', formatter: (name) => { return '{colorBlock| } {txt|' + name + '}'; }, textStyle: { rich: { colorBlock: { backgroundColor: '#3A7BFD', width: 12, height: 12, borderRadius: 6, padding: [0, 0, 0, 0] }, txt: { color: '#D3E1FF', fontSize: 13, padding: [0, 0, 0, 6] } } } }这段配置和前面用●字符不一样的地方在于,colorBlock里没有放任何字符,而是通过富文本的backgroundColor配合width、height画了一个矩形块,再用borderRadius: 6把它变成圆角矩形甚至圆形。
这样一来,图例的颜色完全由富文本的backgroundColor控制,和 series 的颜色没有任何关系了。柱子的渐变、阈值变色,都不会再影响图例。而且每个图例项可以单独声明不同的 rich 样式名,实现“第一个圆点蓝色,第二个圆点黄色”这种需求。
这个方法唯一的限制是:富文本的样式是静态写在textStyle.rich里的,不能像 HTML 那样直接写行内样式。但实际项目中,系列数量有限,为每个系列预定义几个样式名完全够用。
4. 进阶玩法:柱状图叠加折线图时的 legend 统一
4.1 混合图表图例形态乱怎么办
做数据大屏时,柱状图叠加折线图是高频需求。这种图表里,柱子系列的默认图例是矩形,折线系列的默认图例是线条加小标记,两种形态混在一起,视觉风格很不统一。
推荐的做法是把所有图例形状统一成同一种。比如都改成圆点:
legend: { icon: 'circle', itemWidth: 10, itemHeight: 10 }注意折线图系列的图例颜色取的是lineStyle.color,不是itemStyle.color。如果你设置了柱子颜色,又单独设置了折线的线条颜色,那么图例会自动根据各自的系列取色,不需要额外处理。
如果折线图里数据点的小标记和线条颜色不一致,比如线条是蓝色、数据点是白色,图例默认取的是线条颜色。这个细节很多新人会忽略:想让图例显示数据点的颜色,需要把lineStyle.color和数据点的颜色保持一致。
还有一种情况是柱子和折线都走渐变,图例的渐变糊成一片。这时候第四章的 formatter 富文本或者自定义 HTML 图例就派上用场了,把所有图例统一成实色圆点,看起来会干净很多。
4.2 自定义 HTML legend,跳出 canvas 的限制
原生 legend 不管怎么调,总归是在 canvas 里绘制,样式能力有限。当需求上升到“图例要带阴影、要渐变色块、要图标加文字换行、要 hover 高亮背景”这种程度,原生 legend 就力不从心了。这时候我通常直接关掉原生 legend,在图表容器旁边自己画一套 HTML 图例。
关掉原生图例只需要:
legend: { show: false }然后在页面里准备一个普通的 div 当作图例容器,用 JavaScript 渲染图标和文字。
<div id="chart" style="width: 100%; height: 400px;"></div> <div id="customLegend"></div>const chart = echarts.init(document.getElementById('chart')); const seriesConfig = [ { name: '实际销量', color: '#3A7BFD' }, { name: '目标销量', color: '#FFB938' } ]; function renderLegend(selectedMap) { const el = document.getElementById('customLegend'); el.innerHTML = seriesConfig.map(s => { const isActive = selectedMap ? selectedMap[s.name] !== false : true; return ` <span class="legend-item ${isActive ? 'active' : ''}">.legend-item { display: inline-flex; align-items: center; margin-right: 16px; cursor: pointer; color: #A3B5D6; font-size: 13px; user-select: none; } .legend-dot { width: 10px; height: 10px; border-radius: 50%; margin-right: 6px; } .legend-item.active { color: #D3E1FF; } .legend-item:not(.active) { opacity: 0.5; }4.3 交互联动与事件清理
自定义 HTML 图例的关键点是交互联动:点击 HTML 图例 → 派发legendToggleSelect动作 → ECharts 内部切换系列显隐 → 触发legendselectchanged事件 → 回调里重新渲染 HTML 图例的高亮状态。
这套链路有个细节:legendselectchanged事件里params.selected是一个对象,key 是系列名称,value 是布尔值。我上面renderLegend的写法是selectedMap[s.name] !== false,这样在第一次没有传入 selectedMap 时,默认全部显示。
还有一点容易被忽略:如果页面上有多个图表实例,或者图表在路由切换后重新创建,事件监听可能会叠加。单页应用里尤其要小心,每次创建图表实例时,最好在合适的时机调用chart.off('legendselectchanged')再重新绑定,或者直接chart.dispose()销毁实例。
window.addEventListener('resize', () => chart.resize()); // 页面销毁时 chart.dispose();resize事件同理,如果组件卸载后没有移除监听,图表已经被 dispose 了,resize 回调里再调用chart.resize()就会报错。正确做法是把 resize 回调抽成具名函数,销毁时移除监听。
5. 完整实操:渐变柱状图 + 圆点图例的大屏案例
5.1 需求拆解
用一个实际需求来收拢前面所有知识点。
假设我们要做一张大屏图表,展示五个城市的“实际销量”和“目标销量”。需求有四条:
第一,柱子要使用渐变色,从上往下从亮到透明。第二,“实际销量”柱状图的柱子如果超过 400,整根变成警示红。第三,图例要显示成小圆点,颜色和柱子的主色调保持一致,但必须是纯色,不能出现渐变糊掉的情况。第四,整体是深色大屏风格,文字颜色、坐标轴颜色都要适配。
5.2 完整代码
const chartDom = document.getElementById('chart'); const chart = echarts.init(chartDom); const seriesConfig = [ { name: '实际销量', color: '#3A7BFD' }, { name: '目标销量', color: '#FFB938' } ]; const cities = ['北京', '上海', '广州', '深圳', '杭州']; const actualData = [320, 280, 430, 390, 510]; const targetData = [350, 300, 400, 380, 480]; function buildSeries() { return seriesConfig.map((item, index) => { const rawData = index === 0 ? actualData : targetData; const data = rawData.map(v => { // 实际销量超过 400 的柱子,整根变警示色 const isOver = index === 0 && v > 400; return { value: v, itemStyle: isOver ? { color: '#F56C6C' } : { borderRadius: [4, 4, 0, 0], color: { type: 'linear', x: 0, y: 0, x2: 0, y2: 1, colorStops: [ { offset: 0, color: item.color }, { offset: 1, color: 'rgba(58, 123, 253, 0.15)' } ] } } }; }); return { name: item.name, type: 'bar', barWidth: 16, data }; }); } chart.setOption({ backgroundColor: '#0B1230', grid: { left: 48, right: 24, top: 60, bottom: 32 }, tooltip: { trigger: 'axis' }, legend: { top: 16, right: 24, icon: 'none', itemWidth: 14, itemHeight: 14, formatter: (name) => { const idx = seriesConfig.findIndex(s => s.name === name); return `{dot${idx}|●} {txt|${name}}`; }, textStyle: { rich: { dot0: { color: '#3A7BFD', fontSize: 14, padding: [0, 6, 0, 0] }, dot1: { color: '#FFB938', fontSize: 14, padding: [0, 6, 0, 0] }, txt: { color: '#D3E1FF', fontSize: 13 } } } }, xAxis: { type: 'category', data: cities, axisLine: { lineStyle: { color: '#2A3A6A' } }, axisLabel: { color: '#A3B5D6' } }, yAxis: { type: 'value', splitLine: { lineStyle: { color: '#1D2B50' } } }, series: buildSeries() });5.3 关键点复盘
这段代码是整套知识点的综合应用。
系列配置抽成了seriesConfig数组,颜色只维护一份。柱子的渐变起始色取自item.color,图例富文本dot0、dot1的颜色也手动对齐了seriesConfig里的颜色。如果后面要改颜色,只改seriesConfig里的一项就行,所有地方同步。
阈值标红的逻辑放在buildSeries的数据映射里。注意红色柱子同时失去了渐变效果,这是有意的:弹出警示超过 400 的柱子,用高饱和纯色才能第一时间抓住眼球。如果你希望红色也是渐变,可以把color也换成LinearGradient对象,改成红色系从亮到暗。
图例部分用icon: 'none'关闭了默认图标,再通过 formatter 返回{dot0|●}或{dot1|●}。富文本里dot0和dot1分别配了不同颜色,所以实际销量显示蓝色圆点,目标销量显示黄色圆点。因为 formatter 里用的是findIndex动态计算序号,即使seriesConfig增加系列,只要在textStyle.rich里补上对应的dot2、dot3样式就能继续工作。
这套代码拉到深色大屏环境里,字体颜色、轴线和背景色都做了适配,标题区域如果还需要加,直接在backgroundColor之上再加一个title配置即可。
6. 常见问题与排查技巧
6.1 高频问题速查表
| 问题现象 | 可能原因 | 解决方案 |
|---|---|---|
| 图例颜色和柱子颜色对不上 | 数据项单独设置了 itemStyle.color,legend 取的是 series 整体颜色 | 把 series.itemStyle.color 设置为和图例期望一致,或用 formatter 富文本覆盖图例颜色 |
| 柱子用了渐变后图例很糊 | legend 把 LinearGradient 对象应用到小图标上 | 用 icon:'none' 配合 formatter 画纯色圆点,或自定义 HTML 图例 |
| 图例项点击后没反应 | legend.data 的 name 和 series.name 不一致 | 检查 name 是否完全一致,包括空格 |
| 图例多了就换行或者消失 | legend 默认不滚动分页 | 设置 legend.type:'scroll' |
| 大屏上文字不随屏幕缩放 | canvas 里的文字不会自动适配 rem | 监听 resize,根据容器宽度动态计算 fontSize |
| 图表初始化后图例空白 | 容器初始化时处于 display:none 状态 | 确保容器可见后再 init,或延迟初始化 |
| 自定义 HTML 图例点击失效 | 事件绑定时图表实例已被销毁 | 使用具名回调函数,销毁时 chart.off() |
| path 自定义图标位置偏移 | path 坐标范围不标准 | 将 path 坐标统一调整到 0-1024 区间 |
6.2 调试工具与排查思路
ECharts 调试里最实用的是官方社区和实例编辑器。遇到图例样式问题,先删掉业务代码里的样式干扰,用最小化配置复现,比如只保留一个 series、一个 legend,逐步加回其他配置,很快能定位到是哪一层配置导致的。
还有一个容易踩的坑:初始化图表时容器被某个 loading 遮罩盖住了,导致图例区域宽高为 0。这种问题看代码很难看出来,直接在浏览器 DevTools 里用元素检查看容器实际尺寸就行。
如果图例的某个 item 始终不显示,检查两步。第一步,看 series 的name是不是空字符串,空字符串会导致图例无法正确匹配。第二步,看legend.data里有没有漏掉某个系列,手动配置 legend.data 时漏写是最高频原因。
6.3 我在实际项目里踩过的坑
我自己做大屏项目时,图例相关的问题排前三的分别是:渐变图例糊、多系列颜色对不上、图例文字和整体设计风格不搭。前两个问题在上面的速查表里都有解,第三个问题是我后来下定决心用自定义 HTML 图例的直接原因。
原生 legend 的textStyle能控制的样式有限,无法做不同图例项不同颜色、不同字号,也无法让图例项和旁边的内容对齐。换成 HTML 图例之后,这些问题全部变成普通的 CSS 问题,设计稿什么样就能还原成什么样。
还有一个小经验:图例数据如果是异步加载的,一定要在setOption之后、数据返回之前的这段时间里先把图例容器撑起来,否则数据回来后容器尺寸突变,图表可能闪烁。最简单的做法是给图例容器一个min-height,或者在 loading 状态时就渲染好空状态图例。
最后分享一个我常用的配置习惯:把 seriesConfig 抽成公共配置数组,series、legend、tooltip 的回调都从这个数组里取值。颜色、名称、是否显示、图标形状全部收敛到一处,改需求时只需要改一个地方。这套方案在我多个项目里验证过,省下的沟通和时间成本非常可观。如果你也经常和 ECharts 图例打交道,建议下次重构时试试。