uni-app中图片碎片化消失动画必须用canvas实现:先drawImage绘图,再getImageData按行列切块并存为带alpha/偏移的对象,通过requestAnimationFrame驱动每块独立衰减;需修正dpr与缩放映射点击坐标,且canvas宽高须显式设置。

uni-app里用canvas实现图片碎片化消失动画最可靠
纯CSS或uni.createAnimation无法真正“切图+逐片消失”,必须借助canvas手动绘制像素块并控制每块的透明度/位移。这是唯一能精确控制碎片数量、形状、消失时序的方式。
常见错误是试图用transform对多个image标签做错位动画——实际渲染会卡顿,且无法响应点击位置触发局部破碎;还有人用v-for生成上百个view模拟碎片,内存暴涨,iOS上直接白屏。
- 碎片逻辑必须在
canvas中完成:先ctx.drawImage把图片画进去,再用ctx.getImageData读取像素,按行列切割成pieceWidth × pieceHeight的小矩形 - 每个碎片存为对象:
{ x, y, width, height, alpha: 1, offsetX, offsetY },动画时只更新alpha和偏移量 - 务必在
onReady后获取canvas上下文,且需在uni.createSelectorQuery中用in指定组件实例,否则真机找不到画布
监听点击位置并映射到canvas坐标要修正dpr和缩放
用户点的是屏幕坐标,但canvas绘图坐标受设备像素比(uni.getSystemInfoSync().pixelRatio)和css层缩放影响,不转换会导致碎片从错位位置炸开。
关键步骤是:用uni.createSelectorQuery().select('#myCanvas').boundingClientRect()拿到canvas在页面中的真实left/top/width/height,再结合event.touches[0].clientX计算相对canvas左上角的归一化坐标:
const rect = res; const xInCanvas = (touchX - rect.left) * (rect.width / rect.width); // 注意:这里需除以css宽,乘以canvas实际宽 const realCanvasWidth = this.canvasWidth; // 你设的canvas.width属性值 const realCanvasHeight = this.canvasHeight; const normX = (xInCanvas / rect.width) * realCanvasWidth; const normY = (yInCanvas / rect.height) * realCanvasHeight;
- canvas的
width和height属性必须显式设置(如300),不能只靠css拉伸,否则getImageData读取区域错乱 - 微信小程序真机需调用
canvasToTempFilePath前先draw一次,否则截图为空 - H5端注意
touchstart事件可能被catchtouchstart阻止,建议用@touchstart.stop
碎片动画用requestAnimationFrame而非setInterval
setInterval在页面后台或低帧率设备上会堆积定时器,导致动画突跳甚至卡死;requestAnimationFrame与屏幕刷新同步,且自动暂停不可见时的执行。
每个碎片应独立维护自己的动画状态,用时间戳驱动线性衰减:
this.pieces.forEach(piece => {
const elapsed = Date.now() - piece.startTime;
if (elapsed
- 不要用
v-for绑定pieces数组再v-show控制显示——DOM频繁增删比canvas重绘更耗性能 - 碎片数建议控制在
5×5到10×10之间,超过12×12安卓低端机明显掉帧 - 动画结束后的清理:把
alpha === 0的碎片从数组中splice掉,避免后续每帧遍历无用数据
不同平台canvas路径和导出逻辑差异必须分条件处理
微信小程序的canvas是native canvas,H5是web canvas,App(vue2)走的是plus.webview桥接,三者toTempFilePath行为完全不同。
- 微信:必须传
canvasId,且tempFilePath返回本地临时路径,可直接uni.previewImage - H5:没有
toTempFilePath,得用canvas.toDataURL('image/png')转base64,注意长度限制(超8MB可能失败) - App端:需用
uni.canvasToTempFilePath并指定success回调,且必须确保canvas已绘制完成(加setTimeout或监听draw完成事件) - 所有平台都要在动画结束后清空canvas:
ctx.clearRect(0, 0, width, height),否则残留图像会影响下一次绘制
碎片化不是炫技,而是交互反馈。最容易被忽略的是:点击后立刻禁用按钮或遮罩层,防止重复触发;以及在onUnload里销毁requestAnimationFrame句柄,否则离开页面后动画仍在后台运行,消耗电量。











