Canvas画饼图需手动闭合路径:arc()仅绘弧线,必须配合moveTo(圆心)和closePath()(或lineTo(圆心))形成扇形;漏掉beginPath()会导致路径重叠;起始角需偏移-Math.PI/2对齐12点方向,后续扇形角度累加计算。

Canvas 画饼图不是调个 API 就完事,arc() 只负责画一段弧,扇形得你手动闭合、填色、防重叠——漏掉 beginPath() 或没连回圆心,出来的就是“弧线拼贴”,不是实心扇形。
为什么扇形总缺两条边?
只调 arc() 不加 moveTo() 和 closePath(),Canvas 默认用直线把弧的两个端点连起来,形成的是“弓形”(弓背是弧,弓弦是直线),不是从圆心出发的扇形。
- 必须在
arc()前调ctx.moveTo(cx, cy),把起点设到圆心 - 必须在
arc()后调ctx.closePath()(或ctx.lineTo(cx, cy)),让路径真正闭合为三角扇形 - 每个扇形都要独立
beginPath(),否则上一个扇形的路径会干扰下一个的fill()
起始角怎么对齐 12 点钟方向?
Canvas 的 0 弧度默认指向 3 点钟方向(正右),但饼图习惯从顶部开始。不偏移的话,第一块永远从右边冒出来。
- 统一把起始角设为
-Math.PI / 2(即 -90°),相当于把 0° 转到 12 点方向 - 后续扇形的
startAngle就是上一块的endAngle,累加计算,别重置 - 最后一块建议兜底:如果累计弧度
,直接把 <code>endAngle设为2 * Math.PI,避免因浮点误差留白缝
颜色分段填充常见踩坑
填错色、颜色溢出、相邻扇形粘连,基本都是 Canvas 状态机特性没管住。
立即学习“前端免费学习笔记(深入)”;
- 每次绘新扇形前必须重设
ctx.fillStyle,不能依赖上次值 - 别在同一个
beginPath()里画多个扇形再统一fill()——那样会按非零环绕规则填充整个复合路径,结果是整圆变一种色 - 如果要描边分隔,
ctx.strokeStyle = '#fff'+ctx.lineWidth = 1+ctx.stroke()放在fill()后,且只对当前闭合路径生效 - 高亮某一块时,不要清画布重画,只需保存该扇形的
startAngle和endAngle,重绘时临时改fillStyle即可
文字标签总跑偏怎么办?
百分比文字不是随便一放就行,坐标得从扇形中点方向算,还要考虑对齐基线。
- 中点弧度 =
startAngle + (endAngle - startAngle) / 2 - x =
cx + Math.cos(midAngle) * radius * 0.7,y =cy + Math.sin(midAngle) * radius * 0.7(0.7 是离圆心距离系数) - 务必设
ctx.textBaseline = 'middle'和ctx.textAlign = 'center',否则文字上下左右都可能错位 - 高分屏下记得同步设置
canvas.width/canvas.height为物理像素,并用ctx.scale(devicePixelRatio, devicePixelRatio)补偿,否则文字模糊或偏移
真正难的不是算角度,而是每一块扇形都得当做一个独立路径来管理:清空、定位、画弧、闭合、填色、写字——少一步,图形就不可控。尤其在加交互时,状态叠加会让问题更隐蔽。



















