必须成套绑定touchstart/touchmove/touchend并统一用touches[0]读坐标,touchstart中立即preventDefault()且设touch-action: pan-x,slide宽度需精确固定以确保回弹对齐。

touchstart/touchmove/touchend 必须成套绑定,缺一不可
只监听 touchmove 是无效的——浏览器会默认触发页面滚动,touchmove 甚至可能被吞掉或延迟。必须在 touchstart 中就调用 event.preventDefault(),才能让后续的 touchmove 和 touchend 持续被捕获。
常见错误是只在 touchmove 里加 preventDefault(),结果 iOS Safari 下完全不触发,Android Chrome 则卡顿或偶发失效。
- 三个事件要绑定到同一元素,共享状态变量(如
isDragging = false) -
touchstart中必须检查e.touches.length === 1,避免多指干扰单滑动逻辑 -
touchstart里立即存e.touches[0].pageX和当前transform值,别等touchmove再算
坐标读取必须用 touches[0],不是 changedTouches[0]
滑动过程中,changedTouches 可能为空或不稳定(尤其 iOS 会合并多次 touchmove),用它算位移会导致跳变、方向误判或卡顿。
正确做法是全程依赖 e.touches[0].pageX:起始、移动、结束都用它,保证坐标源一致。
立即学习“前端免费学习笔记(深入)”;
-
touchstart:存startX = e.touches[0].pageX -
touchmove:算deltaX = e.touches[0].pageX - startX -
touchend:仍用e.touches[0].pageX(此时手指刚离开,touches还没清空) - 永远先判空:
if (e.touches.length > 0)再取e.touches[0]
容器 CSS 必须设 touch-action: pan-x,不能靠 JS 拦一切
仅靠 preventDefault() 拦 touchmove 是粗暴且易出错的——它会同时锁死竖向滚动,用户想滑轮播图时页面却不能上下滚。
touch-action: pan-x 是更早、更轻量的拦截层,告诉浏览器“这个区域只允许水平拖拽”,既保留竖向滚动,又避免误触发双击缩放或长按菜单。
- 写在容器上:
<div style="touch-action: pan-x"> - 不要写
touch-action: none,除非你真要完全接管所有手势(比如画板) - 旧版 iOS 不支持
pan-x?那就退而用touch-action: manipulation,至少禁用双击缩放 - 搭配
overflow: hidden和display: flex的 track 才能真正对齐 slide 宽度
滑动后回弹不准,90% 是没对齐 slide 宽度
容器宽度是 375px,但每张图设了 width: 100%,父级有 padding 或 border,实际可滚动宽度就不等于整数倍 slide 宽——滑到第三张,累计误差就明显了。
根本解法不是调阈值或加动画,而是让每次 translateX 都落在精确的倍数点上。
- 每个
slide设固定像素宽:width: 375px(或用calc(100vw - 2 * var(--gap))) - track 总宽 =
slideWidth * slideCount,别靠内容撑开 - 松手后 snap 到最近的
slideWidth * index,而不是简单 +50px 就翻页 - 用
requestAnimationFrame做回弹缓动,别只靠 CSStransition,否则无法响应速度衰减
真实滑动交互最难的不是监听事件,而是让每一次手指抬起后,视觉位置和逻辑索引严格对齐——这要求宽度计算、坐标读取、DOM 更新三者零误差。稍有偏差,用户就会觉得“卡”“跳”“跟不上手”。



















