必须用 position: absolute + z-index + transform 实现真实堆叠,因 flex/column 无法脱离文档流致 z-index 失效;需 relative 父容器、每卡 absolute 定位重合、js 动态更新 z-index 与 transform 偏移。

不能只靠 flex-direction: column + justify-content: flex-end 实现视觉堆叠——卡片仍在文档流中,z-index 失效,滑动时交互错乱、层级跳变,iOS Safari 尤其明显。
为什么必须用 position: absolute + z-index + transform
Flex 或 Grid 只能做容器对齐,无法让卡片真正“脱离文档流并逐层叠加”。真实堆叠依赖三个条件同时成立:
-
position: relative的父容器作为定位参考(不能是overflow: hidden或transform非 none,否则触发新层叠上下文,z-index范围被截断) - 每张卡片设
position: absolute、top: 0、left: 0,起始位置完全重合 - 用
z-index控制层级(JS 动态更新,不能靠 CSS 伪类或 nth-child),再用transform: translateX()/translateY()加偏移模拟手滑感
touchmove 中位移计算和防抖关键点
手指一动就卡顿或跳变,90% 出现在 touchmove 处理逻辑里:
- 只在
touchstart记startX = e.touches[0].clientX,不存pageX(受滚动影响) -
touchmove中只算dx = e.touches[0].clientX - startX,直接赋给顶层卡片的style.transform,不做任何 DOM 查询或setState - 必须调
e.preventDefault(),尤其在 iOS 微信和 Safari 中,否则默认滚动会抢事件 - 别用
style.left或style.marginLeft——它们和transform不兼容,会导致回弹错位
移动端动画阈值与回弹怎么配才不反直觉
用户划一半飞走,或划到底才响应,问题不在“动效炫不炫”,而在物理反馈是否匹配手部惯性:
- 阈值别写死:
Math.min(120, window.innerWidth * 0.25)更稳妥;小程序建议 60–80,H5 建议 100–140 - 飞出动画用
transform+opacity,时长 ≤ 300ms,贝塞尔曲线用cubic-bezier(0.22, 0.61, 0.36, 1) - 回弹必须等飞出动画结束回调里执行,不能在
touchend立即splice数组——uni-app/Vue 的响应式更新有延迟,会导致下一张“闪一下”才到位 - Safari 下若父容器有
backface-visibility: hidden或perspective,可能意外截断z-index层级,要检查 computed styles
最易被忽略的是:所有卡片的 z-index 必须由 JS 在每次滑动后**立即重排**,不能依赖 CSS 规则顺序;而 transform 偏移量要随索引差值线性递增(如第 i 张比当前顶卡低 2 层,就 translateX(8px) translateY(-6px)),否则堆叠角度会随数据长度变化而突变。
前端入门到VUE实战笔记:立即使用
在学习笔记中,你将探索 前端 的入门与实战技巧!











