在 uni-app 中,实现图片镜像翻转通常需要借助 canvas 绘制,不能只依赖 CSS transform。正确做法是先在 onReady 中调用 uni.createCanvasContext('myCanvas', this) 绑定 canvas 上下文,再通过 ctx.translate 与 ctx.scale 组合实现图片水平翻转或垂直翻转。同时要注意,必须在 ctx.draw(true, callback) 执行完成后,再调用 canvasToTempFilePath 导出图片,否则导出的仍可能是原始方向。

uni-app 中实现图片镜像翻转,必须通过 canvas 绘制,不能单纯依靠 CSS 的 transform: scaleX(-1) —— 因为 CSS 方式只会改变页面显示效果,导出、保存或上传后的图片方向依旧不会变化。
镜像翻转前必须先获取 canvas 上下文并正确绑定 this
很多开发者常见的错误,是把 uni.createCanvasContext 放在 onLoad 或普通方法中直接调用,导致上下文没有绑定到当前组件实例,最终出现绘图失败或 canvas 无法正常操作的问题。
uni.createCanvasContext('myCanvas', this)的第二个参数this不能省略,否则ctx无法正确关联组件实例,也就不能正常访问 data 与生命周期环境- 推荐必须在
onReady中完成初始化:onReady() { this.ctx = uni.createCanvasContext('myCanvas', this) } - 后续所有与图片处理相关的
ctx.drawImage、ctx.scale、ctx.translate等操作,都依赖这个已经绑定完成的ctx
水平/垂直镜像:使用 ctx.scale(-1, 1) 或 ctx.scale(1, -1)
如果直接调用 ctx.scale(-1, 1),虽然坐标系会被翻转,但图片通常会被绘制到画布外侧的负坐标区域,因此必须配合 ctx.translate 先移动原点,才能正确完成图片镜像效果。
- 水平镜像(左右翻转):
ctx.translate(canvasWidth, 0) ctx.scale(-1, 1)
- 垂直镜像(上下翻转):
ctx.translate(0, canvasHeight) ctx.scale(1, -1)
- 特别注意:
canvasWidth和canvasHeight必须为整数,并且应与目标图片的原始宽高一致。例如图片是 600×400,那么 canvas 的width与height属性也应分别设置为600和400 - 不要仅用 CSS 的
style.width/style.height来控制 canvas 缩放,因为这只会影响显示尺寸,不会影响实际绘制的像素区域
base64 图片加载时机混乱,容易导致绘图结果为空
当使用 new Image() 加载 base64 图片字符串时,onload 有可能会同步触发,尤其是在缓存命中的情况下。这时如果 this.ctx 尚未创建完成,绘制过程就可能直接失败,而且没有明显报错。
- 更稳妥的处理方式是设置标志位,将图片加载和 canvas 绘制逻辑拆开:
this.img = new Image() this.img.onload = () => { this.imgReady = true; this.tryDraw() } this.img.src = 'data:image/png;base64,...' - 在
tryDraw()方法中判断上下文和图片是否都已准备完成:if (this.ctx && this.imgReady) { this.ctx.drawImage(this.img, 0, 0) this.ctx.draw() } - 对于高清 base64 图片,尤其是宽高超过 1000px 的场景,更容易触发内存占用警告,建议提前压缩图片或限制最大尺寸后再进行 canvas 绘制
导出镜像后图片仍然是原方向?重点检查 canvasToTempFilePath 的调用时机
在真机环境中,经常会遇到“明明已经画出了镜像效果,但保存出来还是原图方向”的问题。其根本原因通常是没有等待 ctx.draw() 真正完成,就提前调用了保存接口。
- 正确做法是把保存逻辑放进
draw的回调中:this.ctx.draw(true, () => { uni.canvasToTempFilePath({ canvasId: 'myCanvas', success: (res) => { /* res.tempFilePath 即镜像图 */ } }) }) uni.canvasToTempFilePath在 H5 和小程序平台上的行为基本一致,但在 App 端(iOS/Android)仍需确认当前环境是否支持该 API;如果不支持,可以降级使用canvas.toDataURL(H5)或原生插件方案- 在 iOS 环境下,如果 canvas 宽高为 0,或只设置了显示样式却没有正确设置实际尺寸,保存时很容易得到白图,因此务必检查
的width、height与:style是否保持一致
图片镜像翻转本身并不复杂,真正容易出问题的地方,往往集中在 canvas 上下文绑定、异步执行时机以及像素尺寸对齐这三个关键点。尤其是 this.ctx 的初始化位置,以及 ctx.draw() 回调的使用方式,只要漏掉其中任意一项,就很容易出现“看似绘制成功,实际却没有生效”的情况。
