uni.chooseImage选图必须设count:1且sourceType显式包含['camera','album'],否则iOS微信小程序默认多选、H5可能返回空数组;sizeType优先用['compressed']防卡顿;裁剪需平台适配:微信用wx.cropImage,其他端用canvas并注意clip顺序与pixelRatio补偿,导出须控尺寸、压质量、转jpg,禁用base64上传。

uni.chooseImage 选图时必须设 count: 1 和双 sourceType
不加 count: 1,iOS 微信小程序会默认弹出多选界面,用户选了多张,res.tempFiles[0] 却只取第一张,毫无提示;H5 端则可能直接返回空数组。sourceType 只写 ['album'],iOS 真机上相机按钮不显示——必须显式写成 ['camera', 'album'],微信才按顺序渲染两个按钮。另外,sizeType: ['compressed'] 要优先启用,避免原图动辄 5MB+ 导致后续 canvas 渲染卡顿或内存溢出。
裁剪不能靠“插件一键搞定”,得区分平台选方案
微信小程序有原生 wx.cropImage,最稳:src 传 tempFilePath,cropScale: '1:1' 直接出圆角正方形图,不用自己算坐标、不用 canvas、不兼容问题。但这个 API 仅限微信小程序,其他平台(App、H5、支付宝等)不可用。跨端项目若强行统一用第三方裁剪组件(如 una-img-cropper),要注意:它在 H5 端依赖 document,App 端可能因 WebView 内核缺失 Canvas 2D 上下文而报错;真机调试时 iOS 微信内嵌页常静默失败,无任何 error 抛出。
所以实际策略是:
- 微信小程序:优先走
wx.cropImage,成功后直接uni.uploadFile - App/H5:用
una-img-cropper或自研 canvas 裁剪,但必须包裹v-if="platform !== 'mp-weixin'"隔离 - 所有平台都需 fallback:若裁剪失败(比如用户取消、canvas 初始化失败),降级为直接上传原图,并前端提示“已跳过裁剪,将上传原始图片”
clip() 必须在 drawImage() 前调用,且圆心坐标是 canvas 坐标系
自己写 canvas 裁剪时,这是最高频的白屏原因。错误写法:ctx.drawImage(img, ...); ctx.beginPath(); ctx.arc(...); ctx.clip(); —— 此时 clip() 对已绘制内容无效,最终 canvas 是空的。正确顺序必须是:
ctx.beginPath(); ctx.arc(centerX, centerY, radius, 0, 2 * Math.PI); ctx.clip(); // ⚠️ 这行必须在 drawImage 之前 ctx.drawImage(image, sx, sy, sw, sh, dx, dy, dw, dh);
注意:centerX/centerY 是相对于 canvas 元素左上角的像素位置,不是原始图片坐标;sx/sy 是从原图截哪一块(比如缩放后居中区域),dx/dy 是把它画到 canvas 上的落点——这两组参数共同决定圆形区域内最终显示哪一部分原图。真机上还要乘 pixelRatio 补偿高清屏,否则裁剪框偏移。
裁剪后导出要控尺寸、压质量、转格式,别直接传 base64
用户拍的图宽高常超 2000px,裁剪后若直接 canvas.toDataURL('image/jpeg', 1),生成的 base64 字符串可能超 5MB,H5 端内存爆掉,小程序端上传直接 413 Request Entity Too Large。正确做法:
- 导出宽度建议 ≤ 400px(头像够用),
outputWidth: 400是una-img-cropper的安全值 - 质量设
quality: 0.85,比默认 0.92 更省流量,肉眼几乎无损 - 强制转
fileType: 'jpg',避免透明 PNG 上传后背景变黑(头像不需要 alpha) - filePath 必须用
uni.canvasToTempFilePath或插件返回的临时路径,别用 base64 发给后端——base64 在微信小程序里有 2MB 限制,且后端解析成本高
最后提醒一句:微信小程序的 wx.cropImage 返回的是新临时路径,有效期仅 24 小时,必须在拿到后立刻上传,不能缓存或延迟触发 uni.uploadFile。


















