不能直接用@longtap,因h5无该事件、app端响应不稳定、小程序与权限流程耦合;须用touchstart+touchend+定时器手动模拟,并严格校验图片加载、路径、权限及平台差异。

直接用 @longtap 绑定图片,在 H5 会静默失效,App 端(尤其 iOS)响应不稳定,微信小程序虽支持但和权限、下载流程耦合紧密——必须手动实现一套跨端一致的长按逻辑,否则真机必踩坑。
为什么不能直接用 @longtap
@longtap 是 Vue 指令级封装,底层依赖各平台原生事件:微信小程序有原生长按菜单,App 端需系统级支持(iOS 常延迟或漏触发),H5 根本不触发——浏览器没有该事件。更麻烦的是,它和图片加载状态、触控手势(比如双指缩放误判为单指)、平台 API 调用时机完全脱节。常见现象是:H5 点击没反应,App 真机长按后无提示、无保存、控制台也无报错。
解决方案是放弃 @longtap,统一用 touchstart + touchend + 定时器模拟:
- 在
touchstart中启动一个 800ms 的setTimeout(比系统默认 1s 更灵敏) - 在
touchend或touchcancel中清除定时器 - 仅当
e.touches.length === 1时才允许计时(排除滑动、缩放干扰) - 触发前检查
this.imgLoaded[index] === true,避免保存空白图
uni.saveImageToPhotosAlbum 只认本地路径,网络图必须先下载
uni.saveImageToPhotosAlbum 的 filePath 参数只接受本地文件路径(如 tempFilePath 或 _doc/xxx.png),传 https:// 地址会报 fail: invalid file path,且错误信息模糊,真机调试时容易误判为权限问题。
正确流程是分三步走:
- 调用
uni.downloadFile({ url }),成功后取res.tempFilePath - 必须检查
res.statusCode === 200,否则临时路径无效(常见于 HTTPS 证书不被信任、域名未备案、图片超 10MB) - 微信/支付宝小程序需提前在后台配置「downloadFile 合法域名」,否则请求直接拦截
- 切勿用
uni.getImageInfo替代——它返回的是缓存路径,部分安卓机型无法被相册 API 识别
权限检查不能省,iOS 拒绝后无法二次弹窗
iOS 上若用户首次点击「不允许」,uni.authorize 后续调用将静默失败,控制台无日志,uni.saveImageToPhotosAlbum 直接跳 fail 回调。Android 部分 ROM(如华为 EMUI)也会静默拒绝。
必须严格按顺序执行:
- 先
uni.getSetting读取res.authSetting['scope.writePhotosAlbum'] - 若为
undefined或false,立即uni.authorize请求权限 - 若
authorize失败(用户点“不允许”),必须立刻uni.openSetting跳转设置页——这是 iOS 14+ 后唯一补救方式 - 权限检查和保存逻辑不能写在同一层回调里,否则异步顺序错乱会导致「刚授权完就调保存,但权限还没生效」
H5 端根本没有相册概念,得走下载逻辑
uni.saveImageToPhotosAlbum 在 H5 下直接报 saveImageToPhotosAlbum is not a function 或静默忽略。所谓“保存”,本质是触发浏览器下载行为。
安全写法是:
- 对同域图片,用
@#@#@#@#@#@#@#@#@#@0+click() - 对跨域图片(如 CDN),必须服务端加
Access-Control-Allow-Origin: *,否则canvas绘制会污染,toDataURL报错 - 禁用
data:URL(如 base64),Safari 会忽略download属性 - 不要幻想 H5 实现“长按识别二维码”——只有原生
<image></image>标签在小程序里才支持该能力
最易被忽略的一点:所有平台的图片加载完成状态(imgLoaded)必须显式监听 @load 和 @error,否则网络慢时长按触发了,但图片还没加载完,下载的就是空白内容。这个状态管理一旦漏掉,真机复现率 100%。











