最稳方案是直接用 uni-cropper 并设 mode="circle",因其已封装 EXIF 处理、像素对齐、clip 时机等跨端细节;手动 canvas 易在 iOS 方向、真机模糊、H5 渲染等环节翻车。

直接用 uni-cropper 并设 mode="circle",是目前最稳、最少踩坑的方案。自己手写 canvas 容易在 iOS 方向异常、真机模糊、H5 不渲染等环节翻车。
为什么别急着自己 canvas 画圆
手动实现看似可控,但实际要处理一堆跨端细节:EXIF 方向(iOS 拍照后常逆时针转 90°)、canvas 像素对齐(ctx.arc 参数不是整数就发虚)、clip() 调用时机(必须在 drawImage 前)、H5 下临时路径无法直读等问题。很多项目卡在“裁出来是方的”或“真机黑屏”,本质是漏了其中一环。
常见错误现象包括:
- 微信小程序真机报错
canvas is not defined—— 检查manifest.json是否开了「增强编译」 - 裁剪后边缘模糊 —— canvas 宽高或
arc的centerX/radius不是整数 - iOS 图片旋转错位 —— 没解析 EXIF 的
Orientation字段,也没做对应旋转
uni-cropper 配置要点(v2.0+ 版本)
这是目前社区维护最活跃、适配最全的 uni-app 裁剪组件(注意不是 npm 上同名的老版本)。它把圆形裁剪封装成一个开关,不用碰 canvas。
关键配置项:
-
mode="circle":必须显式设置,否则默认矩形 -
imgWidth和imgHeight:建议设为300左右,避免高清图拖慢渲染 -
aspectRatio不用设 —— 圆形模式下自动忽略 - 图片源必须用
ref.cropper.setSrc(imagePath)动态注入,不能靠 props 直传src,否则 H5 下不触发重绘
裁剪结果是 base64 字符串,若需上传,优先走 uni.uploadFile({ filePath: tempFilePath }) 路径方式(先用 uni.getFileSystemManager().writeFile 存临时文件),比 base64 传更省内存。
如果非要用 canvas 手动裁(比如定制遮罩动画)
核心逻辑只有四步,但每步都有硬约束:
- canvas 元素宽高设为整数,且与目标输出尺寸一致(如裁 200×200 圆图,
canvas.width = canvas.height = 200) - 绘图前调
ctx.scale(1, 1)清除潜在缩放(尤其 iPhone X 等设备) - 用
ctx.arc(100, 100, 100, 0, Math.PI * 2)画圆,三个参数都必须是整数 -
ctx.clip()必须在ctx.drawImage()之前调用,顺序反了等于没裁 - 导出用
canvas.toDataURL('image/png', 1),第二个参数强制为1,否则默认压缩会糊边
EXIF 处理不能省:iOS 图片必须用 exif-js@2.3.0 解析 Orientation,6 就顺时针转 90°,8 就逆时针转 90°,再调整 drawImage 的坐标和旋转角度。
真正难的不是“怎么画圆”,而是让同一套代码在 H5、微信小程序、App 三端都输出清晰、方向正确、不闪不卡的圆图。插件已经帮你扛了 90% 的兼容性问题,手动 canvas 只适合极少数需要深度定制遮罩或动画的场景。











