Canvas绘制饼图需手动计算角度并逐扇形绘制:先用beginPath()开启路径,moveTo(圆心)后调用arc()画弧,再lineTo(圆心)或closePath()闭合形成实心扇形;起始角应偏移-Math.PI/2以对齐12点方向;每次绘制前必须beginPath()防止路径污染,文字标签通过中点角与三角函数计算坐标并设置对齐方式。

Canvas 本身不提供 drawPie() 这类高级接口,所有饼图都得手动算角度、连路径、逐扇形填充——但只要抓住“圆心→弧度→路径闭合”这个链条,就能稳定输出正确图形。
为什么扇形画出来像“西瓜皮”,而不是实心扇形?
常见错误是只调用 ctx.arc(),没配合 ctx.moveTo() 指向圆心,导致浏览器只画了一段弧,两端直接直线连接(即 SVG 里的“劣弧自动闭合”行为),结果就是缺了两条半径边。
- 必须在
ctx.arc()前加ctx.moveTo(centerX, centerY) - 必须在
ctx.arc()后调用ctx.closePath()或再次ctx.lineTo(centerX, centerY) - 每个扇形都要独立
ctx.beginPath(),否则样式和路径会污染下一个
角度怎么算才对齐 12 点钟方向?
Canvas 的 arc() 默认从 0 弧度(3 点钟方向)开始逆时针画,但人眼习惯饼图从顶部(12 点)开始。所以起始角要偏移 -Math.PI / 2。
- 总和 =
data.reduce((a, b) => a + b, 0) - 单个弧度 =
(val / total) * Math.PI * 2 - 实际起始角 = 上一个结束角(初始为
-Math.PI / 2) - 结束角 = 起始角 + 单个弧度
为什么填色溢出或扇形粘连?
根本原因是 Canvas 是状态机:fillStyle、路径、甚至当前点位置都会延续到下一次绘制。漏掉 beginPath() 或重复使用同一路径对象,就会让多个扇形共用一条路径,fill() 时按奇偶规则填充整个区域。
立即学习“前端免费学习笔记(深入)”;
- 每次画新扇形前必须
ctx.beginPath() -
ctx.fill()前不要混用stroke(),除非你明确想描边 - 如果需要高亮某一块,建议单独保存该扇形的起始/结束角,在重绘时临时改
fillStyle,别试图“擦除重画”
文字标签怎么居中写进扇形里?
Canvas 没有自动布局,文字坐标得自己算:取扇形夹角中点方向,沿半径往外偏移一段距离(比如 radius * 0.6),再用三角函数转成 x/y。
- 中点角 =
startAngle + sliceAngle / 2 labelX = centerX + Math.cos(midAngle) * radius * 0.6labelY = centerY + Math.sin(midAngle) * radius * 0.6- 用
ctx.textAlign = 'center'和ctx.textBaseline = 'middle'对齐更稳
最易被忽略的是 DPI 适配:高分屏下若只设 HTML 的 width/height 属性,Canvas 会拉伸模糊。务必同步设置 JS 中的 canvas.width 和 canvas.height 为物理像素值,并缩放 ctx.scale() 补偿。



















