核心是用 touchstart 记起点、touchmove 实时位移、touchend 判阈值触发动画;层叠靠动态 zIndex 计算而非静态堆叠;阈值与动画需匹配物理惯性,全端兼容需针对性处理渲染与手势差异。

怎么用 touchstart/touchmove/touchend 控制卡片位移
核心不是“加动画”,而是先让卡片能跟着手指走——这三步事件必须串起来,缺一不可。很多卡顿或跳变,其实是 touchmove 里做了不该做的事。
-
touchstart只记两个值:startX(初始 X 坐标)和isAnimating状态(防止动画中又被拖拽打断) -
touchmove里只干一件事:算offsetX = currentX - startX,然后直接赋给顶层卡片的transform: translateX(${offsetX}px);别在这里调setState或触发重排 -
touchend才判断逻辑:如果Math.abs(offsetX) > 80(阈值可配),就执行飞出动画;否则调animateTo回弹
注意:微信小程序里 touchmove 默认不冒泡,得加 catchtouchmove;H5 端要防默认行为(e.preventDefault()),否则会触发页面滚动。
为什么 Stack + zIndex 动态计算比 v-for + absolute 更稳
层叠不是靠“堆一堆绝对定位卡片”就能实现的,真正在滑动时,你永远只操作“当前最上层那一张”,其余卡片是靠数据索引动态推算位置和层级的。
- 用
Stack(HarmonyOS)或position: absolute(uni-app)做容器,但每张卡片的zIndex、scale、opacity必须由其与currentIndex的差值实时计算,比如:zIndex: 3 - Math.abs(i - currentIndex) - 硬写
.card:nth-child(1) {z-index: 3}看似简单,但轮播到第 10 张时,DOM 顺序早乱了,样式规则完全失效 - uni-app 中若用
v-for渲染全部卡片,又不做key管理或虚拟列表,iOS 小程序容易白屏或内存爆掉
所以实际结构是:只渲染 3 张可见卡片(前/中/后),用 computed 算出它们的 index 映射,再绑定样式——不是“画满一屏”,而是“精准喂三张”。
滑动阈值和回弹动画怎么配才不反直觉
用户一划就飞走,或者划老远还不响应,问题大概率出在阈值和动画衔接上。这不是调参游戏,而是匹配人手物理惯性。
- 阈值别死守
80:H5 屏幕大,建议120;小程序屏幕小,60更合适;用Math.min(120, windowWidth * 0.25)动态算更稳 - 飞出动画时长控制在
300ms内,太慢像卡住;回弹必须带缓动(cubic-bezier(0.22, 0.61, 0.36, 1)),直线回弹会显得机械 - 千万别在
touchend里直接splice数组再forceUpdate:uni-app 的响应式更新有延迟,会导致下一张卡片“闪一下”才到位;应该先动画移出,动画结束回调里再更新数据源
真实手感来自“松手即响应”,而不是“松手等半秒才动”。动画启动必须在 touchend 同步触发,不能进异步队列。
全端兼容时最常崩在哪几个点
写一套代码跑 H5、微信、支付宝、App,层叠滑动是兼容性重灾区。崩得最隐蔽的,往往不是功能,而是视觉错位或手势吞没。
- 微信小程序里
transform: scale()在 iOS 上对image标签有渲染 bug,虚化要用filter: blur(2px)替代,别依赖opacity混合 - App 端(尤其是 Android)
touchmove频率不稳定,建议加个节流:只在deltaTime > 16(约 60fps)时才更新位移 - H5 端如果用了
perspective做 3D 效果,记得父容器加transform-style: preserve-3d,否则所有子卡片都扁平化,层叠感消失 - 支付宝小程序不支持
animateTo,得降级为setTimeout+requestAnimationFrame手写简易动画,否则飞出动作直接卡死
真正难的不是做出效果,是让每张卡片在不同引擎里,都保持相同的“出手重量感”和“落地停顿感”——这得靠真机反复调,模拟器看不出问题。










