Canvas绘制动态进度饼图需扇形切割与中心遮罩:先用moveTo、lineTo和arc构建闭合扇形路径,起始角为-π/2;再用同心小圆覆盖实现空心效果;通过requestAnimationFrame插值更新进度,并添加百分比文字和描边增强可读性。

Canvas 绘制动态进度饼图,核心在于两个关键操作:用扇形路径控制填充范围(即“扇形切割”),再用同心小圆或矩形遮罩实现视觉上的“空心”效果(即“中心遮罩”)。这不是简单画个圆再涂色,而是通过路径裁剪与分层绘制达成精准的进度表达。
扇形切割:从起始角到当前进度角的路径闭合
绘制进度扇形必须明确三点:圆心坐标、半径、以及当前进度对应的弧度。Canvas 的 arc() 方法本身只画圆弧,要形成可填充的扇形,需配合 moveTo() 和 lineTo() 构建封闭路径:
- 先调用 beginPath() 清除上一路径状态
- 用 moveTo(cx, cy) 将起点移到圆心
- 用 lineTo(cx + r * cos(start), cy + r * sin(start)) 连接到起始点(通常为顶部,即 -π/2)
- 再用 arc(cx, cy, r, startRad, endRad) 画出圆弧
- 最后 lineTo(cx, cy) 回到圆心,完成三角扇形闭合
注意:起始角常设为 -Math.PI / 2(正上方),这样进度从12点钟方向顺时针展开,符合直觉。结束角 = 起始角 + 当前进度 × 2π,比如 60% 进度对应 -Math.PI / 2 + 0.6 * 2 * Math.PI。
中心遮罩:用同心小圆“挖空”中间区域
所谓“遮罩”,并非 Canvas 原生 mask(需额外 canvas 或 clipRegion),而是更轻量的覆盖绘制法:在大扇形绘制完成后,立即用白色(或背景色)填充一个更小的同心圆。
- 小圆半径一般取大圆半径的 30%–50%,例如 rSmall = r * 0.4
- 务必在同一个 beginPath() → fill() 流程中完成小圆,避免描边干扰
- 若需边缘柔和,可对小圆添加 shadowBlur 或用 globalCompositeOperation = 'destination-out' 实现真正裁剪(但兼容性略低)
动态更新:requestAnimationFrame 比 setInterval 更平滑
用 setInterval 容易因帧率抖动导致动画卡顿;推荐使用 requestAnimationFrame 驱动逐帧重绘:
- 定义目标进度值(如 0.75 表示 75%)
- 每帧按比例插值当前进度:current = Math.min(current + 0.01, target)
- 清空画布(clearRect())、重绘扇形、再盖小圆
- 当 current ≥ target 时停止循环
这样能适配不同设备刷新率,动画更自然,也便于后续加入缓动函数(如 easeOutCubic)提升质感。
颜色与文字标注:别忽略视觉引导
纯图形缺乏语义,建议同步添加两项辅助信息:
- 进度百分比文本:用 fillText() 居中写在遮罩圆内,字体加粗、颜色对比明显(如深灰 #333)
- 外圈描边或渐变:给扇形边缘加 2px 描边(strokeStyle + stroke()),或用 createLinearGradient() 做高光过渡,增强立体感
这些细节不增加复杂度,却显著提升可读性与专业度。


















