移动端滑动方向检测时为什么上下滑动会误触左右事件?

Designer°悦辰 阅读 50

我在做移动端左右滑动切换卡片的功能时遇到了问题。用了touchstart记录初始坐标,touchend计算dx和dy差值,但有时候上下滑动也会触发左右切换事件。比如看文章时下拉刷新却被切到上一张卡片。

尝试用绝对值比较判断:if (Math.abs(dx) > Math.abs(dy)),但当滑动角度接近45度时仍然会出错。还试过设置距离阈值,但快速滑动时dx值有时会突然跳变…


let startX, startY;
element.addEventListener('touchstart', e => {
  startX = e.touches[0].clientX;
  startY = e.touches[0].clientY;
});
element.addEventListener('touchend', e => {
  const dx = e.changedTouches[0].clientX - startX;
  const dy = e.changedTouches[0].clientY - startY;
  if (Math.abs(dx) > 50) {
    dx > 0 ? swipeLeft() : swipeRight();
  }
});

有没有更好的方向判断方式?或者需要添加什么条件过滤?感觉现在的逻辑在斜向滑动时特别容易出错…

我来解答 赞 10 收藏
二维码
手机扫码查看
2 条解答
诸葛红佑
你的问题在于判断逻辑太简单了,斜向滑动时 dx 和 dy 绝对值接近,谁大谁小全看运气。

核心问题有两个:一是没设置最小距离阈值,手指轻轻碰一下也会触发;二是只用绝对值比较,在 45 度附近确实会来回横跳。

更靠谱的做法是同时满足两个条件:水平距离要明显大于垂直距离,而且水平距离本身要超过阈值。

改一下你的代码:

let startX, startY;

element.addEventListener('touchstart', e => {
startX = e.touches[0].clientX;
startY = e.touches[0].clientY;
});

element.addEventListener('touchend', e => {
const dx = e.changedTouches[0].clientX - startX;
const dy = e.changedTouches[0].clientY - startY;

// 最小距离阈值,太短的滑动忽略
if (Math.abs(dx) < 30 && Math.abs(dy) < 30) return;

// 判断水平滑动距离是否明显大于垂直距离
// 这里用 1.5 倍而不是 1 倍,给斜向滑动一些容错空间
if (Math.abs(dx) > Math.abs(dy) * 1.5) {
dx > 0 ? swipeLeft() : swipeRight();
}
});


还有个进阶玩法是加入速度判断,防止那些手指已经移开但事件还在触发的情况:

let startX, startY, startTime;

element.addEventListener('touchstart', e => {
startX = e.touches[0].clientX;
startY = e.touches[0].clientY;
startTime = Date.now();
});

element.addEventListener('touchend', e => {
const dx = e.changedTouches[0].clientX - startX;
const dy = e.changedTouches[0].clientY - startY;
const time = Date.now() - startTime;

// 过滤掉滑动距离太短的情况
if (Math.abs(dx) < 30 && Math.abs(dy) < 30) return;

// 过滤掉滑动时间太长的情况(防止慢速拖动误触)
if (time > 500) return;

// 斜向容错 + 速度补偿
if (Math.abs(dx) > Math.abs(dy) * 1.5) {
dx > 0 ? swipeLeft() : swipeRight();
}
});


基本上这样改完,正常左右滑动切换卡片没问题,下拉刷新也不会误触了。你那个 45 度角的问题,用 1.5 倍这个系数能过滤掉大部分情况。
点赞
2026-03-11 22:00
爱学习的红会
问题出在只判断距离不够,还要加滑动角度过滤。直接用atan2计算角度,限制在45度范围内才触发。

let startX, startY;
element.addEventListener('touchstart', e => {
startX = e.touches[0].clientX;
startY = e.touches[0].clientY;
});
element.addEventListener('touchend', e => {
const endX = e.changedTouches[0].clientX;
const endY = e.changedTouches[0].clientY;
const dx = endX - startX;
const dy = endY - startY;
const angle = Math.atan2(Math.abs(dy), Math.abs(dx)) * 180 / Math.PI;
if (angle < 45 && Math.abs(dx) > 50) {
dx > 0 ? swipeLeft() : swipeRight();
}
});


角度判断加上去基本就够了,省得写一堆乱七八糟的阈值判断。实在不行再加个时间阈值过滤下快速滑动的情况。
点赞 8
2026-02-14 22:00