Canvas画圆环进度动画的关键是正确使用ctx.arc的弧度参数并显式调用draw()重绘:起始角固定为-Math.PI/2,结束角为-Math.PI/2+2Math.PIprogress,且需设置lineWidth、strokeStyle,并在onReady中初始化ctx,通过clearActions()和stroke()实现逐帧动画。

uni-app里Canvas画圆环进度动画的关键是ctx.arc参数顺序
直接用 ctx.arc 画圆环时,很多人卡在「为什么只画了一小段弧,或者方向反了」——根本原因是起始角和结束角单位是弧度,且 Canvas 默认逆时针绘制。进度为 30% 时,不能写 startAngle = 0, endAngle = 30,而要换算:endAngle = 2 * Math.PI * 0.3。
常见错误现象:
• 圆环从 12 点钟方向开始但动画往左走(逆时针)
• 进度到 50% 时弧长远小于半圈(没转弧度)
• stroke() 后线条极细甚至看不见(没设 lineWidth)
- 起始角建议固定为
-Math.PI / 2(即从顶部 12 点钟方向开始) - 结束角 =
-Math.PI / 2 + 2 * Math.PI * progress,这样进度 0→1 对应 0°→360° 顺时针增长 - 务必在
stroke()前设置ctx.lineWidth和ctx.strokeStyle - 使用
ctx.setLineWidth()在某些安卓低端机上无效,优先用ctx.lineWidth = ...
uni-app中Canvas组件需手动触发draw,不能靠data响应式自动重绘
很多人把 progress 写进 data,以为修改它就能重画——不行。uni-app 的 canvas 是原生渲染层,必须显式调用 uni.createCanvasContext + draw() 才生效。
使用场景:适合需要逐帧控制的进度动画(比如上传、加载),不适合纯 CSS 能搞定的静态环。
- 在
onReady里初始化 canvas context,存到this.ctx,避免每次重复创建 - 进度变化时,先
this.ctx.clearActions()(清空旧指令),再重绘路径+stroke - 动画用
setTimeout或requestAnimationFrame控制帧率,别直接 for 循环改 progress - 真机调试时,
uni.canvasToTempFilePath在微信小程序里需指定canvasId,H5 端不支持该 API
uni-app跨端Canvas兼容性坑:H5端不支持canvasId动态绑定
H5 端的 canvas 组件不识别 :canvas-id 动态绑定,必须写死字符串,否则 uni.createCanvasContext('myCanvas') 拿不到上下文。
错误写法:<canvas :canvas-id="canvasId"></canvas> → H5 下 ctx 为 null
正确写法:<canvas canvas-id="myCanvas"></canvas>,且 JS 中固定用 'myCanvas'
- 微信小程序、App、H5 三端都支持
canvas-id字符串字面量,但仅 H5 不支持绑定 - 支付宝小程序要求 canvas 必须设
width和height属性(带单位,如"300px"),否则渲染为空 - 部分安卓 WebView 对
globalCompositeOperation支持差,做遮罩叠加时慎用
简单进度环动画的最小可行代码结构
不用封装成组件,50 行内能跑通。重点不是炫技,而是让圆环动起来且跨端可用。
export default {
data() {
return {
progress: 0
}
},
onReady() {
// 注意:H5 不支持 :canvas-id,这里写死
this.ctx = uni.createCanvasContext('progressCanvas', this)
},
methods: {
startAnimation() {
let p = 0
const timer = setInterval(() => {
p += 0.02
this.progress = Math.min(p, 1)
this.drawRing()
if (p >= 1) clearInterval(timer)
}, 50)
},
drawRing() {
const ctx = this.ctx
ctx.clearRect(0, 0, 300, 300) // 清画布
ctx.beginPath()
ctx.arc(150, 150, 100, -Math.PI/2, -Math.PI/2 + 2*Math.PI*this.progress)
ctx.setStrokeStyle('#4cd964')
ctx.setLineWidth(12)
ctx.stroke()
ctx.draw()
}
}
}容易被忽略的一点:Canvas 的像素密度在 retina 屏上会模糊,如果需要高清,得根据 uni.getSystemInfoSync().pixelRatio 缩放 canvas 宽高并调整绘图坐标,但多数进度环没必要——先保证动起来,再考虑清晰度。


















