news 2026/10/1 20:14:02

ECharts饼图/环形图配置:radius、legend与labelLine

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
ECharts饼图/环形图配置:radius、legend与labelLine

饼图这个东西,看起来是 ECharts 里最简单的一类图表,但真到了要调样式的时候,很多人会卡在同样的几个地方:饼图大小怎么调都感觉不对劲、图例总是压在饼图上、指示文字挤成一团、引导线末尾的小圆点怎么都对不齐。我自己在后台报表里画了不下几十个饼图和环形图,从最开始的"能显示就行"到后来开始抠像素级的细节,中间踩的坑基本都集中在 radius、center、legend 和 labelLine 这四个配置簇上。这篇就把这些配置项按实际使用顺序拆开讲清楚,顺便把 pxtorem 对 ECharts 不起作用、容器塌陷、tooltip 不换行这些高频问题一并说透。适合已经能用 ECharts 画出饼图、但想让它在真实业务页面里更稳更整齐的前端同学;如果你刚开始接触 ECharts,文中的配置也能直接抄走用。

1. 饼图配置的三个战场:series、legend、tooltip 各管什么

1.1 先搞清楚 ECharts 配置项的分层逻辑

很多人改饼图样式的效率低,根本原因不是不熟悉 API,而是没搞清楚一个问题该改哪个字段。ECharts 的配置是分层覆盖的,饼图的视觉结果由三层叠加而成:最外层是option的全局配置,中间层是legend、tooltip、title这些"周边组件",最内层才是series[i]里属于饼图自己的配置。

举个最容易混淆的例子:你想让饼图的标签文字变小。这个"标签文字"到底是legend.textStyle、tooltip.textStyle,还是series.label.fontSize?三者都叫"文字样式",但管的东西完全不同——legend.textStyle管图表上方或右侧那排图例的名字,tooltip.textStyle管鼠标悬浮时弹出的浮层,series.label才是围着扇形那一圈。我见过不少人改了legend.textStyle.fontSize之后发现扇形旁边的字一点没变,然后开始怀疑是不是版本问题。

同理,"饼图大小"这件事也不是单一字段能决定的。series.radius控制圆本身多大,series.center控制圆心在容器的哪个位置,而容器本身的宽高又是由你的 CSS 决定的。三者任一变化,视觉结果都会变。把它当成一个三方博弈来理解,调起来会顺很多。

还有一层容易忽略的是默认值。ECharts 给饼图设了一批默认值,这些默认值在简单场景下很好用,但在定制场景里就是干扰项。下面这张表是我最常需要主动覆盖的几个默认行为:

配置项默认行为什么时候必须改
radius约为容器较短边的 75%容器是扁长形,或旁边要放图例
center['50%', '50%']容器正中图例占了顶部或右侧空间
startAngle90,即从 12 点方向逆时针起画想让占比最大的扇形从右侧开始
avoidLabelOverlaptrue数据项极少时想精确控制标签位置
minAngle0有极小占比的数据项,标签会挤在一起
label.position'outside'环形图要在圆心放汇总数字

提示:饼图的radius、center里的百分比,参照物是容器的宽和高中较小的那个,不是宽。这一点和很多人直觉里的"按宽度算"不一样,也是扁形容器里饼图看起来偏小的原因。

1.2 饼图最容易被忽略的三个默认值

第一个是startAngle。默认 90 意味着第一个数据项从 12 点方向开始,并且是逆时针排布。绝大部分设计稿画的饼图是从右侧 3 点方向顺时针走的,这时候你得把startAngle设成 90 加上一个偏移,或者干脆改成-90之类的值去试。别小看这个,扇形顺序错了,做出来的图虽然"能看",但和设计稿一比就是不对味。

第二个是avoidLabelOverlap。它默认开启,会自动把挤在一起的标签上下推开,代价是引导线的角度会变得很怪,短扇形对应的标签可能被甩到很远的地方。数据项少于 5 个、每项占比都还行的时候,我一般会把它关掉,自己用labelLayout去控。数据项十几个的小占比场景,才需要它来兜底。

第三个是itemStyle.borderWidth默认是 0,也就是扇形之间没有缝隙。做环形图时加上 2px 的白色描边和圆角,质感会立刻不一样,这个后面细说。

2. 饼图大小到底由谁决定:radius、center 和容器三方博弈

2.1 百分比半径与像素半径的适用边界

radius支持两种写法:字符串百分比,比如'70%';以及数字,单位是像素,比如120。环形图还能写成数组,['40%', '70%']表示内半径和外半径,中间挖空。

选哪种写法,判断标准只有一个:这个图表的尺寸会不会随窗口变化。响应式布局里,容器宽度是不确定的,这时候必须用百分比,让饼图跟着容器缩放。反过来,如果图表被放在一个固定宽高的卡片里,或者你在做可视化大屏的某个固定格子,用像素值反而更可控,因为百分比会随着容器高度变化而变,你精心调好的和旁边文字的间距就飘了。

一个很实用的习惯:先确定"我要留多少边距给标签"。饼图是唯一一种标签画在图形外面的常用图表类型,radius设得太大,外面的标签就会被容器裁掉或者溢出。经验值是,如果标签是单行短文字,外半径控制在容器较短边的 60%~70%;如果标签是两行、还带数值和百分比,最好压到 55%~60%。剩下的空间才是留给 label 和 labelLine 的。

环形图的内半径也值得说一说。内半径太小,中间挖的洞不够,看起来还是像个厚饼;内半径太大,环太细,颜色块显得弱。我一般把内半径设成外半径的 55%~70%,这个比例做出来的环视觉重量比较舒服。中间要放汇总数字的话,内半径可以再大一点,给文字留出足够的水平空间。

// 环形图:内外半径都用百分比,跟随容器缩放 series: [{ type: 'pie', radius: ['58%', '76%'], // 内半径 58%,外半径 76% center: ['50%', '50%'], startAngle: 90 }]

2.2 容器塌陷、resize 失效与 pxtorem 不生效的真实原因

这是我最想单独拎出来讲的一节,因为几乎所有"饼图显示不正常"的问题,根子都不在 ECharts 上,而在容器上。

容器塌陷:ECharts 初始化时会去读取容器元素的clientWidth和clientHeight,如果这两个值里有一个是 0,它画出来的 canvas 就是 0 宽或 0 高,页面上一片空白。最常见的触发场景有三个:父元素用了height: 100%但祖先链上没有确定高度;元素当时处于display: none(比如被v-if控制、在未激活的 Tab 页里);以及用 flex 布局时父容器没有给子项明确的尺寸约束。这三种情况的排查方式都一样——打开控制台看一眼容器元素的盒模型,高度是不是 0。

// 容器必须有确定高度,ECharts 不会替你撑开 .chart-box { width: 100%; height: 320px; /* 不要写 100%,除非祖先链高度是确定的 */ }

resize 失效:窗口尺寸变了但图表没跟着变,99% 是忘了监听。window.addEventListener('resize', () => chart.resize())是最基础的写法,但它有两个毛病:一是只监听窗口,容器自身因为布局变化(比如侧边栏折叠、兄弟元素显隐)而改变宽度时不会触发;二是高频触发会掉帧,需要加节流。更现代的做法是用ResizeObserver直接盯着容器元素:

const box = document.getElementById('chart') const chart = echarts.init(box) let timer = null const ro = new ResizeObserver(() => { clearTimeout(timer) timer = setTimeout(() => chart.resize(), 80) // 简单防抖 }) ro.observe(box)

组件销毁时记得ro.disconnect()和chart.dispose(),不然在单页应用里反复进出这个页面会累积一堆监听器和 canvas。

pxtorem 对 ECharts 不起作用:这个问题被问得特别多。postcss-pxtorem这类插件的工作原理是扫描CSS 文件里的 px 值并替换成 rem,它看不见 JavaScript 里写的数字。而 ECharts 的fontSize: 12、itemWidth: 25这些值全都是 JS 对象里的数字,在 canvas 上绘制,完全不经过 CSS 的转换流程。所以你把fontSize写成'1rem'也没用——ECharts 的字号只接受数字,传字符串会解析失败,退回到默认值。

正确做法是自己在 JS 里算一个基准值,并且随容器宽度更新。这就是"图文自适应"的常见套路:

function getBaseFont(chartWidth) { // 以 375px 设计稿为基准,最小不低于 10,最大不超过 16 const size = (chartWidth / 375) * 12 return Math.min(16, Math.max(10, size)) } // 每次 resize 时重新计算并 setOption function render(chart, data, width) { const fs = getBaseFont(width) chart.setOption({ series: [{ type: 'pie', radius: ['58%', '76%'], label: { fontSize: fs, lineHeight: fs + 4 }, labelLine: { length: fs * 1.2, length2: fs * 1.2 } }] }) }

关键点是:字号、引导线长度、图例项尺寸这些"跟文字走"的参数,应该一起缩放,只缩字号不缩引导线,小屏幕上引导线会显得特别长,很突兀。

2.3 环形图宽度与多个饼图并排时的尺寸协调

一个页面里放两个饼图做对比,是很常见的需求,也很容易做丑。最常见的问题是两个饼图大小不一致——明明用的是同样的radius百分比,为什么一个看起来大一个看起来小?因为百分比参照的是各自容器的较短边,两个容器的高度如果不一样,出来的直径就不一样。解决办法有两个:给两个容器写死同样的高度;或者干脆把radius写成像素值,明确指定直径。

并排时还有个细节是center。默认center是容器正中,但如果你在饼图旁边(不是上方)放了图例,就得把center往反方向挪,否则饼图和图例互相挤。比如图例放在右侧 16px 处,那center的 x 可以设成['42%', '52%']之类的值,让饼图整体左移一点。这种微调没有什么公式,就是打开页面看着改,但你要知道该改哪个字段。

多饼图共存还有一点:如果它们共享一个 tooltip 或共享图例,建议把series.name区分清楚,图例的formatter也要跟着处理,不然两个系列的图例项混在一起,用户根本分不清哪个是哪个。

3. 图例位置与样式:把 legend 摆到该摆的地方

3.1 legend 定位的坐标系与 orient 的联动

legend的位置由left、top、right、bottom四个属性控制,这几个属性既接受'left'、'center'、'right'、'top'、'middle'、'bottom'这样的关键字,也接受百分比和像素值。默认情况下图例在容器顶部居中显示,横向排列。

这里有个特别容易搞混的点:left: 'center'和left: '50%'不是一回事。写'center'是"水平居中"的语义,ECharts 内部会按图例自身宽度计算偏移;写'50%'是把图例的左边缘放到容器宽度的 50% 处,结果会偏右一半宽度。很多人调图例位置怎么调都对不齐,就是因为混用了这两种写法。我的习惯是,能用关键字就用关键字,需要精确偏移时用right: 16或bottom: 8这种从边缘起算的数值,最不容易错。

orient决定图例是横排还是竖排,和位置强相关:

位置推荐 orient说明
顶部居中'horizontal'默认组合,图例项少时最省空间
底部居中'horizontal'适合标题在上、图例在下的版式
右侧贴边'vertical'项数多时首选,几乎不侵占饼图空间
左侧贴边'vertical'适合右侧有说明文字的布局

右侧竖排是我在后台系统里用得最多的一种。它的好处是图例和饼图的边界非常清晰,图例项数量从 3 个到 15 个都不会打架,只要把type设成'scroll'就能自动出现翻页按钮。

legend: { type: 'scroll', orient: 'vertical', right: 16, top: 'middle', // 垂直居中 itemWidth: 10, itemHeight: 10, itemGap: 12, icon: 'circle' }

3.2 图例项过多时的滚动与多列排布

饼图的图例一旦超过 10 项,问题就来了。横向排布会换行,换行之后图例区域变高,把饼图往下挤;纵向排布在容器不够高的时候会被裁掉。这两种情况都需要处理。

设置type: 'scroll'之后,ECharts 会给图例加翻页按钮,超出部分隐藏,点击翻页查看。这个方案适合项数不确定的场景,但我个人不太喜欢——翻页按钮的存在会让用户意识到"这里有内容被藏起来了",在数据看板上容易引起误解。

另一个思路是主动控制图例的显示方式,做多列纵向排布。ECharts 没有直接的"列数"配置,但可以利用legend在不同容器高度下自动换行的特性:把图例放在容器下方,给足宽度,设orient: 'horizontal'和较小的itemGap,它会自动折成两行或三行。用grid是不行的,饼图不吃grid,位置还是靠center手动让位。

还有一种更彻底的做法:如果图例项特别多(比如超过 20 个),饼图本身就已经不适合了,应该考虑换成横向条形图。这不是配置能解决的问题,是图表选型的问题。我见过把 30 个类别的数据硬塞进一个饼图里,最后图例占了大半屏,扇形小得像纸屑,用户根本读不出信息。

图例文字太长也是个典型问题。类目名带前缀或后缀(比如"华东区-直营网点-上海"),图例会被撑得很宽。这时候可以用formatter截断:

legend: { formatter: (name) => name.length > 10 ? `${name.slice(0, 10)}…` : name }

但要注意,formatter截断只影响图例显示,tooltip 里的名称还是完整的,用户点开还能看到全名,体验上是可接受的。如果业务上不允许截断,那就得换成右侧竖排加滚动,把空间换出来。

3.3 图例图标、文字与间距的细节调优

图例的视觉细节里,我改得最多的三个是icon、itemWidth/itemHeight和itemGap。

icon的默认值是一个圆角矩形色块。饼图的图例我更推荐换成'circle',因为扇形本身就是弧形,圆形图标的视觉语言更统一。可选值还有'rect'、'roundRect'、'triangle'、'diamond'、'pin'、'arrow'等等,一般'circle'和'roundRect'够用了。如果想要更细的控制,icon还接受 SVG 路径字符串,可以画一个自定义形状。

itemWidth和itemHeight默认是 25 和 14,这个尺寸在常规字号下是协调的,但当你的图表整体变小(比如手机端)或者变大(比如大屏),就一定要跟着调。我通常的算法是让图标高度接近字号的 0.85 倍左右,比如fontSize: 12时itemHeight: 10、itemWidth: 10,做出来是个小圆点,比默认的扁矩形更精致。

itemGap控制图例项之间的间距,默认 10。纵向排布时我一般加到 12~16,让每一项之间有呼吸感;横向排布时保持在 10~12,太大会导致换行过早。

文字样式写在legend.textStyle里:

legend: { icon: 'circle', itemWidth: 10, itemHeight: 10, itemGap: 14, textStyle: { color: '#5b6472', fontSize: 12, lineHeight: 18, padding: [0, 0, 0, 4] // 图标与文字之间的额外间距 } }

注意:legend.textStyle在 ECharts 5 里仍然是这个字段名,但有些旧教程会写legend.textStyle以外的变体,比如把颜色写到legend.textStyle.color之外的地方,那是不生效的。碰到"改了没反应"的情况,先确认字段名和层级对不对。

还有一个小技巧:legend.inactiveColor可以设置图例被点击取消选中后的颜色,默认是灰色。在做"只看某一类"的交互时,把这个值调成和背景接近的浅灰,视觉上更干净。

4. 指示线与标签文字:饼图颜值的分水岭

4.1 label 的位置选择与 formatter 富文本

series.label是饼图里配置项最多的一个对象,也是最影响最终效果的地方。第一件事是选position:

  • 'outside':默认值,标签在扇形外面,配合引导线使用。适合项数不多的场景。
  • 'inside':标签在扇形内部,不显示引导线。适合占比差异大、且扇形本身够大的场景。
  • 'center':所有标签都堆在圆心。这个值听起来奇怪,但做环形图时非常有用——配合emphasis可以做出"高亮某项时圆心显示该项数值"的效果。

判断标准很简单:扇形的弧长够不够放下一行字。占比低于 5% 的扇形,'inside'的标签一定会溢出或被裁掉,这种数据就该走'outside'。

formatter决定标签显示什么。支持字符串模板和回调函数两种形式。字符串模板里的占位符有{a}(系列名)、{b}(数据项名)、{c}(数值)、{d}(百分比)。简单场景够用:

label: { formatter: '{b}\n{d}%' }

但真实业务里往往要更复杂——比如百分比小于 1% 时不显示数值只显示名称,或者给数值加单位。这时候必须用函数:

label: { formatter: (p) => { const pct = p.percent.toFixed(1) return pct < 1 ? `{name|${p.name}}` : `{name|${p.name}}\n{val|${pct}%}` }, rich: { name: { color: '#3d4451', fontSize: 12, lineHeight: 18 }, val: { color: '#8a93a3', fontSize: 11, lineHeight: 16 } } }

rich是我认为饼图配置里性价比最高的一个功能。它允许你在同一个 label 里用{styleName|内容}的语法,给不同部分套不同样式——不同颜色、不同字号、甚至加背景色和圆角。两行标签(第一行名称、第二行数值)能用它做出很清晰的层次,比单纯用\n换行好看太多。

用rich时有两个坑要注意。第一,rich里的lineHeight一定要显式设置,否则多行文本的行距会按字号自动推算,往往偏挤。第二,rich里设置了height的片段(比如用来做背景色块的),它的垂直位置由行高决定,容易出现上下不居中的情况,用padding微调最稳。

4.2 labelLine 长度、折角与末端小圆点偏移的处理

引导线由labelLine控制,主要字段就三个:length(靠近扇形的那一段)、length2(靠近文字的那一段)、smooth(折角的圆滑程度,0 到 1 之间,越大越圆)。

调整长度的时候有个原则:length和length2的比值决定了引导线的"形状感"。两段一样长的时候,引导线是标准的"两段折线",像工厂里那种折角标签;length短、length2长的时候,引导线几乎是水平伸出去的,适合文字特别长的场景。我一般按length: 12、length2: 16起步调。

smooth建议给一个 0.3 到 0.6 之间的值。默认的直角折线在视觉上有点硬,稍微圆滑一点会让整个图看起来更柔和,尤其是标签比较多的时候,一堆直角折线叠在一起会很乱。

真正麻烦的是"引导线末端的小圆点"。ECharts 的labelLine本身不提供在末端画圆点的配置,网上很多实现都是绕道做的:在 label 文本最前面加一个圆点字符或者用rich画一个小方块,视觉上这个圆点就出现在引导线的末端。

label: { formatter: (p) => `{dot|}{name|${p.name}} {val|${p.value}}`, rich: { dot: { width: 6, height: 6, borderRadius: 3, backgroundColor: '#4a90e2', padding: [0, 6, 0, 0] }, name: { fontSize: 12, lineHeight: 18, color: '#3d4451' }, val: { fontSize: 12, lineHeight: 18, color: '#8a93a3' } } }

这个圆点最常见的毛病就是"上下偏移"——圆点比文字高一点或者低一点,看起来像没对齐。根因在于,rich里设置了height的片段,它占据的是一块独立的盒子,ECharts 在行内布局时会以这一行里的最大高度为基准做对齐,圆点的 6px 盒子和文字 18px 的行高差了一截,位置自然就飘了。

处理方法有两个,看你的具体版本表现来决定:

  1. 把圆点的height设成和同行文字lineHeight一致(比如都是 18),再用borderRadius保证是圆——但这样圆点会变成直径 18 的大点,需要配合width也设 18、然后靠padding收敛。这个做法比较绕。
  2. 更省事的做法是保持圆点小尺寸,然后用padding的上下值微调。上下各试 1px、2px,很快就能对齐。这个方法的好处是可控,坏处是要在目标浏览器和 ECharts 版本上实际看一眼。

我个人的经验是,圆点直径取 6px、文字行高取 18px 时,给dot加padding: [6, 6, 0, 0]这种上边距,在多数版本里能对上。如果项目里组件库会升级 ECharts 版本,建议把这个对齐逻辑封装成一个变量,升级后集中调一次,而不是散落在几十个图表配置里。

如果你想要的是"引导线起点带圆点"的效果(也就是贴着扇形那头的端点),那个其实是可以用itemStyle.borderWidth配合borderColor做出类似视觉的,或者干脆接受没有圆点的默认样式。别在这上面耗太久,这类装饰性细节的收益远不如把标签内容的层次理清楚。

4.3 标签重叠、引导线穿越与极值数据的处理

标签重叠是饼图最常见的视觉问题,触发条件基本就是三个:数据项太多、占比差异太大、容器太小。处理顺序应该是从数据侧往样式侧走。

数据侧能做的是合并小项。占比低于 2% 的类目在饼图上本来就读不出来,把它们合并成一个"其他",既解决重叠又提升可读性。这是我做过的最有效的一招,比任何配置都管用。

样式侧的第一道防线是minAngle。它的作用是给每个扇形一个最小角度,避免极小占比的扇形变成一条线。设成 3 到 5 度比较合适,但要注意它会让百分比显示和实际角度不一致——扇形看起来比 3% 大,标签却写着 3%。如果业务上对这个敏感,就得在 tooltip 里说明,或者干脆用合并小项的方式。

第二道防线是 ECharts 5 提供的labelLayout:

labelLayout: { hideOverlap: true // 重叠的标签自动隐藏 }

这个配置很好用,代价是被隐藏的标签信息就看不到了。我的用法是把它和 tooltip 结合起来——标签藏了没关系,鼠标悬浮扇形还能看到完整信息。这样在保证画面干净的同时不丢信息。

引导线穿越是另一个容易被忽略的问题。当某个扇形的标签被推到对面去的时候,引导线会横穿整个饼图,看起来非常乱。这个现象在avoidLabelOverlap: true的时候特别容易出现。缓解办法是把avoidLabelOverlap关掉,配合labelLayout.hideOverlap,让重复的标签直接隐藏而不是满世界挪。实测下来这个组合在项数 8 到 15 的场景里表现最稳。

5. tooltip 换行、高亮与一整份可直接复制的配置

5.1 tooltip 自动换行的两种可靠做法

饼图的 tooltip 触发方式是trigger: 'item',作用在单个扇形上。内容格式用formatter控制,字符串模板'{b}: {c} ({d}%)'对付简单场景足够。

真正让人头疼的是长文本换行。默认的 tooltip 容器是white-space: nowrap,也就是说不管你的文字多长,它都会倔强地排成一行,横向撑出去,在小屏幕上直接溢出到屏幕外。两种可靠的解法:

第一种是改textStyle,这是最"原生"的做法:

tooltip: { trigger: 'item', textStyle: { width: 200, // 限制容器宽度 overflow: 'break', // 超出部分换行('truncate' 是截断) lineHeight: 18 } }

第二种是加extraCssText,直接注入 CSS:

tooltip: { trigger: 'item', confine: true, // 限制在容器内,不跑出图表区域 extraCssText: 'max-width: 260px; white-space: normal; word-break: break-all;' }

两种我都用过,效果都稳定。区别在于extraCssText是纯 CSS,浏览器兼容性更可控;textStyle.overflow是 ECharts 自己实现的,跟版本有关。如果你要处理的是中英文混排的长类目名,我建议两个一起上——textStyle控宽度和行高,extraCssText加word-break防止长英文单词撑破容器。

另外confine: true这个配置值得单独提一下。默认情况下 tooltip 会跟随鼠标,靠近容器边缘时会跑出图表区域,甚至盖住旁边的组件。加上confine之后它会被限制在容器内部,在栅格化布局里能让整个页面的层级关系清爽很多。

5.2 高亮态、选中态与点击联动

饼图的高亮(emphasis)默认会把扇形的亮度提一点,同时标签加粗。这个默认效果比较克制,实际项目里我一般会加强一些:

emphasis: { scale: true, // 高亮时扇形轻微放大 scaleSize: 6, // 放大 6px itemStyle: { shadowBlur: 12, shadowColor: 'rgba(0, 0, 0, 0.18)' }, label: { fontWeight: 'bold' } }

scale配合scaleSize做出的"弹出"效果,在点击交互里特别有用。用户点某个扇形时它微微凸出来,交互反馈非常明确。注意scaleSize的单位是像素,不要设太大,3 到 8 之间比较自然,超过 10 会显得很浮夸。

选中态可以用selectedMode来做。饼图支持selectedMode: 'single'和'multiple',开启后点击扇形会切换选中状态,选中项默认会偏移出去(selectedOffset控制偏移距离,默认 10px)。这个特性适合做"只看某一类"的筛选场景,比如点击某个区域的扇形,右侧的明细列表跟着刷新。

series: [{ type: 'pie', selectedMode: 'single', selectedOffset: 8, // ... }]

配合chart.on('click', ...)拿到点击项,就能做联动:

chart.on('click', (params) => { if (params.componentType !== 'series') return fetchDetail({ name: params.name, value: params.value }) })

这里有个细节要留意:如果你用selectedMode,点击时扇形会有一个"弹出"的动画,而selectedOffset影响的是位置偏移,视觉上会和emphasis.scale叠加。两个都用的话,看起来会弹得比较夸张。一般选一个就好,我个人更倾向emphasis.scale,因为它只在悬浮时生效,不会改变图表的静态布局。

5.3 一份可以直接抄的完整配置与上线前自检清单

把前面所有内容整合起来,这是一个我实际项目里用的环形图配置模板,右侧竖排图例、圆心预留数值位、标签用富文本做两行、tooltip 限宽换行:

option = { tooltip: { trigger: 'item', confine: true, extraCssText: 'max-width: 260px; white-space: normal; word-break: break-all;', formatter: (p) => `${p.name}<br/>数量:${p.value}<br/>占比:${p.percent.toFixed(1)}%` }, legend: { type: 'scroll', orient: 'vertical', right: 12, top: 'middle', icon: 'circle', itemWidth: 10, itemHeight: 10, itemGap: 14, textStyle: { color: '#5b6472', fontSize: 12, lineHeight: 18 }, formatter: (name) => name.length > 10 ? `${name.slice(0, 10)}…` : name }, series: [{ name: '设备类型', type: 'pie', radius: ['56%', '74%'], center: ['36%', '52%'], startAngle: 90, minAngle: 4, avoidLabelOverlap: false, itemStyle: { borderColor: '#fff', borderWidth: 2, borderRadius: 4 }, label: { show: true, position: 'outside', formatter: (p) => `{dot|}{name|${p.name}} {val|${p.percent.toFixed(1)}%}`, rich: { dot: { width: 6, height: 6, borderRadius: 3, backgroundColor: '#4a90e2', padding: [6, 6, 0, 0] }, name: { fontSize: 12, lineHeight: 18, color: '#3d4451' }, val: { fontSize: 12, lineHeight: 18, color: '#8a93a3' } } }, labelLine: { length: 12, length2: 16, smooth: 0.4, lineStyle: { color: '#c9d0da', width: 1 } }, labelLayout: { hideOverlap: true }, emphasis: { scale: true, scaleSize: 6, itemStyle: { shadowBlur: 12, shadowColor: 'rgba(0,0,0,0.16)' } } }] }

上线前我会过一遍这几个检查项,基本能拦住九成问题:

检查项怎么查出问题的表现
容器高度控制台看盒模型图表完全空白
resize 监听拖动窗口看是否跟随图表被拉伸变形
字号缩放手机模拟器上看小屏文字挤在一起
标签溢出缩小容器宽度标签被裁切
图例遮挡项数拉到最大图例压住饼图
tooltip 宽度用最长的类目名测浮层横穿出屏幕
销毁清理反复进出页面内存持续增长

提示:itemStyle.borderRadius需要 ECharts 5.0 及以上版本;labelLayout同样是 5.0 引入的。如果你的项目还锁在 4.x,这两个配置是不生效的,升级前先用itemStyle.borderWidth加白色描边来做出分隔感。

最后说一个我自己踩过的坑。有一次在一个移动端页面里,饼图怎么调都在右侧留着一大块空白,我改了半小时center都没对。最后发现是容器的padding-right是某个全局样式带进来的,饼图的百分比是相对内容区算的,外墙被 padding 占了。所以,当你觉得"怎么调都不对"的时候,先别改 ECharts 的配置,打开控制台把容器的盒模型和继承来的样式看清楚,往往问题根本不在图表这一层。

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/10/1 20:13:44

Cursor安全插件链:代码审计新范式与TaoToken统一接入

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

作者头像 李华
网站建设 2026/10/1 20:11:05

TDengine Community 超级表建表实战:统一21个TAG与DOUBLE/字符串数据模板

一、背景在数据中台时序数据接入过程中&#xff0c;不同测点的数据类型虽然不同&#xff0c;但设备、区域、系统、点位等业务属性基本一致。因此可以采用 TDengine 超级表统一建模&#xff1a;时间字段数据值质量码固定业务 TAG本项目约定所有超级表统一采用&#xff1a;time v…

作者头像 李华
网站建设 2026/10/1 20:09:30

Linux 下启动 Redis 的正确姿势:从安装到守护进程的完整指南

我第一次在 Linux 服务器上启动 Redis 的场景&#xff0c;现在想起来还挺狼狈的。当时我照着教程敲了个redis-server&#xff0c;看到屏幕上刷出一大串日志&#xff0c;心想“成了”&#xff0c;转头就把终端关了。结果第二天同事问我 Redis 怎么挂了——我就愣在那儿。后来我才…

作者头像 李华
网站建设 2026/10/1 20:07:37

云代理商实战:云端部署的 Hermes Agent 接入 Slack 的网关配置与验证

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

作者头像 李华
网站建设 2026/10/1 20:05:18

【claude code实践】Plugins 入门:扩展 Claude Code 的工具生态

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

作者头像 李华