真实堆叠必须用 position: absolute + z-index + transform 实现,flex/grid 仅视觉叠放而 z-index 无效;需 relative 父容器、js 动态更新层级与偏移,touchmove 中精准计算 dx 并防抖,飞出与回弹需匹配物理反馈。

必须用 position: absolute + z-index + transform 实现真实堆叠
Flex 或 Grid 容器里直接排卡片,哪怕加 flex-direction: column-reverse 或 justify-content: flex-end,也只是视觉上“看起来叠在一起”,实际所有卡片仍在文档流中,z-index 完全无效。滑动时会出现层级跳变、点击错位、iOS Safari 交互卡顿等问题。
真正能支撑手势滑动的堆叠,需要三个条件同时满足:
-
position: relative的父容器(不能带overflow: hidden或非none的transform,否则会意外创建新层叠上下文,截断z-index范围) - 每张卡片设
position: absolute、top: 0、left: 0,起始位置完全重合 - 用 JS 动态更新
z-index和transform偏移量——CSS 规则顺序或伪类无法响应滑动状态
touchmove 中位移计算和防抖关键点
手指一动就卡顿、偏移量不跟手、松手后回弹错位,90% 是 touchmove 处理逻辑写错了:
- 在
touchstart中只记e.touches[0].clientX(别用pageX,滚动时会漂移) -
touchmove中只算dx = e.touches[0].clientX - startX,直接赋给顶层卡片的style.transform,不做任何 DOM 查询或setState - 必须在
touchmove里调e.preventDefault(),尤其 iOS 微信和 Safari,否则默认滚动抢事件 - 别用
style.left或style.marginLeft—— 它们和transform不兼容,会导致回弹错位
阈值、飞出动画与回弹时机怎么配才不反直觉
用户划一半飞走,或划到底才响应,问题不在动效炫不炫,而在物理反馈是否匹配手部惯性:
- 阈值别写死:
Math.min(120, window.innerWidth * 0.25)更稳妥;H5 建议 100–140,小程序建议 60–80 - 飞出动画用
transform+opacity,时长 ≤ 300ms,贝塞尔曲线推荐cubic-bezier(0.22, 0.61, 0.36, 1) - 回弹必须等飞出动画结束回调里执行(如
el.addEventListener('transitionend', handler)),不能在touchend立即splice数组——Vue/uni-app 响应式更新有延迟,会导致下一张“闪一下”才到位 - Safari 下若父容器有
backface-visibility: hidden或perspective,可能意外截断z-index层级,要检查 computed styles
为什么 scroll-timeline 不适合做移动端卡片堆叠
它目前(2026 年)根本不能用于生产环境的跨浏览器层叠滑动:
- 仅 Chrome/Edge 115+ 支持,Firefox 和 Safari 完全不支持,且无 fallback 方案
- 层叠卡片常依赖
position: relative+ 绝对定位子项,这类容器默认不可滚动,强行加overflow-y: scroll会破坏堆叠视觉流 - 当卡片容器设了
overflow: hidden(常见于滑动视口),Chrome 会静默禁用scroll-timeline,控制台无报错,动画直接不触发 -
z-index无法用 CSS 变量绑定滚动进度,必须 JS 动态设style.zIndex;而多卡片的相对偏移、旋转、缩放等,仍需 JS 驱动或大量冗余 CSS 变量模拟
最易被忽略的是:所有卡片的 z-index 必须由 JS 在每次滑动后立即重排,不能依赖 CSS 规则顺序;而 transform 偏移量要随索引差值线性递增(比如第 i 张比当前顶卡低 2 层,就 translateX(8px) translateY(-6px)),否则堆叠角度会随数量变化失真。
前端入门到VUE实战笔记:立即使用
在学习笔记中,你将探索 前端 的入门与实战技巧!











