移动端左右滑动和上下滑动手势冲突怎么处理?

技术秋花 阅读 56

在移动端页面里,同时存在左右滑动切换卡片和上下滑动页面的需求,但左右滑动总是被页面滚动劫持了。我用hammer.js监听了panstart事件,尝试通过event.direction判断方向再调用event.preventDefault(),但有时候左右滑动还是会触发页面滚动…


const hammer = new Hammer(element);
hammer.get('pan').set({ direction: Hammer.DIRECTION_ALL });
hammer.on('panstart', (e) => {
  if (e.direction === Hammer.DIRECTION_HORIZONTAL) {
    e.target.style.touchAction = 'none'; // 这样设置有效吗?
    e.preventDefault();
  }
});

试过给元素加touch-action: pan-y;,但有些安卓机还是不行。有没有更稳定的解决方法?

我来解答 赞 5 收藏
二维码
手机扫码查看
1 条解答
司马彦会
这个问题主要是因为浏览器的默认滚动行为和你自定义的手势监听之间产生了冲突。touch-action: pan-y; 确实是一个正确的方向,但它在某些安卓设备上的兼容性确实不够好。我们可以通过更精细的事件控制来解决。

首先,问题的核心是 panstart 事件触发时,手势的方向可能还没有完全确定,导致 e.direction 判断不准确。另外,e.preventDefault() 在某些情况下并不能完全阻止默认滚动行为。

一个更稳定的解决方案是结合 touch-action 和手势事件的动态判断。具体来说,可以这样处理:

1. 给目标元素设置 touch-action: none;,这会禁用所有默认的触摸行为。
2. 在 panstart 中判断初始滑动方向,如果检测到是水平滑动,就保持禁止滚动;如果是垂直滑动,则允许页面滚动。

下面是改进后的代码示例:


const hammer = new Hammer(element);
hammer.get('pan').set({ direction: Hammer.DIRECTION_ALL });

let isHorizontalPan = false;

hammer.on('panstart', (e) => {
// 根据初始角度判断滑动方向
const angle = Math.abs(e.angle);
if (angle > 45 && angle < 135) {
isHorizontalPan = true;
e.target.style.touchAction = 'none'; // 禁止默认行为
} else {
isHorizontalPan = false;
e.target.style.touchAction = 'pan-y'; // 允许垂直滚动
}
});

hammer.on('panmove', (e) => {
if (isHorizontalPan) {
e.preventDefault(); // 防止触发页面滚动
// 实现你的左右滑动逻辑,比如移动卡片
element.style.transform = translateX(${e.deltaX}px);
}
});

hammer.on('panend', () => {
isHorizontalPan = false;
element.style.transition = 'transform 0.3s ease';
element.style.transform = 'translateX(0)';
});


几点说明:
- 这里通过 e.angle 来判断滑动方向,比直接用 e.direction 更精确。
- 动态设置 touch-action 可以有效避免手势冲突。
- 在 panend 中重置样式和状态,确保下一次滑动正常。

如果你发现某些安卓机型依然有问题,建议再调试看看是否需要对特定设备做兼容性处理,比如强制监听 touchstarttouchmove 原生事件,并手动调用 preventDefault()

最后提醒一下,测试的时候多用真机,模拟器有时候表现不太一样。
点赞
2026-02-16 15:00