uni.previewimage不支持缩放动画,需自行封装:记录原图位置,用transition+transform实现平滑缩放入场,多图滑动时独立维护每张图的缩放状态,并注意性能优化。

uni.previewImage 本身不支持缩放动画,别指望它能平滑放大
uni.previewImage 是原生层调用的图片查看器,打开即全屏、缩放靠双指手势,没有「从原图位置平滑放大到全屏」这类入场动画。你看到的所谓“动画”,其实是平台原生查看器自己的过渡效果(iOS 比较明显,安卓多数无),uni-app 完全无法控制或定制。想实现点击图片后从原位置飞入、渐变放大,必须放弃 uni.previewImage,自己封装。
用 transition + transform 实现点击触发的平滑缩放入场
核心思路是:把原图位置记录下来(getBoundingClientRect()),在遮罩层中动态创建一个绝对定位的 <image></image>,初始 scale=0.1、opacity=0,然后通过 class 切换触发动画。
- 必须用
v-if控制预览层显隐,否则 transition 不会触发 - scale 动画要搭配
transform-origin: center center,否则缩放中心偏移 - transition 建议写成
transform 0.3s cubic-bezier(0.25, 0.46, 0.45, 0.94), opacity 0.3s,比ease更自然 - H5 端记得加
user-select: none,防止长按弹出菜单打断动画 - App 和小程序端需用
uni.createSelectorQuery()替代getBoundingClientRect()获取原图坐标,因为 ref 在某些端可能拿不到真实 DOM
多图滑动时如何保持缩放状态不重置
自己封装的预览器里,每张图都得独立维护 scale、translateX、translateY 状态,不能共用同一套变量。否则左右滑动切换后,上一张图的缩放会被清空,用户刚放大的图又缩回原样。
- 用数组存每张图的状态:
imageStates: [{scale: 1, x: 0, y: 0}, {scale: 2.3, x: -10, y: 5}, ...] - 滑动结束(
@change)后,只重置新图的scale和位移为初始值,旧图状态保留 - 避免在
swiper的current变化时直接v-if销毁整个图片 DOM,否则 transform 状态丢失——改用v-show配合 visibility + pointer-events: none 控制交互 - 缩放最大值建议限制在
3以内,否则 iOS WebView 容易卡顿甚至崩溃
真机上缩放卡顿?先查这三处
不是代码写得不够 fancy,而是平台限制和资源加载问题更常见:
-
image组件 mode 用了widthFix或aspectFill—— 改成scaleToFill或去掉 mode,让图片原尺寸渲染,缩放才顺滑 - 图片宽高超过 2000px,尤其在安卓低端机上,GPU 渲染压力大,建议服务端返回预览尺寸(如 1200w)的 URL
- 没做 touchmove 节流,手指一划就高频触发 translate 计算,CPU 直接拉满;用
requestAnimationFrame包一层,或者简单加个if (Date.now() - lastTime
缩放动画的“平滑”感,80% 来自图片尺寸与设备性能匹配,剩下 20% 才是 transition 曲线和 transform 使用是否恰当。别在 4K 图上硬卷 cubic-bezier。











