手势识别是移动端前端开发中常用的功能,通过监听触摸事件判断用户的操作意图,能实现比传统点击更丰富的交互效果。下面我们就一步步讲解如何在JavaScript中实现常见的手势识别。

手势识别的基础:TouchEvent事件
JavaScript中手势识别依赖TouchEvent事件体系,移动端触摸操作会触发以下核心事件:
touchstart:手指触摸屏幕时触发,可获取初始触摸位置、触摸点数量touchmove:手指在屏幕上滑动时持续触发,可获取实时位置变化touchend:手指离开屏幕时触发,可计算手势的最终结果touchcancel:触摸操作被意外中断时触发,比如来电、系统弹窗
每个TouchEvent事件的touches属性存储当前屏幕上的所有触摸点,changedTouches存储当前事件变化的触摸点,每个触摸点包含clientX、clientY等坐标信息。
常见手势的判断逻辑
1. 点击手势
点击的判断条件比较简单:触摸开始和结束的位置偏移很小,且触摸持续时间较短。通常偏移阈值设为10px,时间阈值设为300ms。
2. 滑动手势
滑动需要判断触摸开始和结束的位置偏移超过阈值,同时根据X、Y轴的偏移量判断滑动方向:
- 水平偏移大于垂直偏移:左右滑动
- 垂直偏移大于水平偏移:上下滑动
3. 双指缩放手势
双指缩放需要监听两个触摸点的距离变化,初始距离和结束距离的比值就是缩放比例,距离计算使用勾股定理:Math.sqrt((x2-x1)^2 + (y2-y1)^2)。
完整实现代码示例
下面是一个封装好的手势识别类,支持点击、滑动、双指缩放三种手势:
class GestureRecognizer {
constructor(element) {
this.element = element;
// 触摸起始信息
this.startPoints = [];
this.startTime = 0;
// 配置阈值
this.tapMaxDistance = 10; // 点击最大偏移px
this.tapMaxDuration = 300; // 点击最大时长ms
this.swipeMinDistance = 30; // 滑动最小偏移px
this._bindEvents();
}
_bindEvents() {
this.element.addEventListener('touchstart', this._handleTouchStart.bind(this), { passive: false });
this.element.addEventListener('touchmove', this._handleTouchMove.bind(this), { passive: false });
this.element.addEventListener('touchend', this._handleTouchEnd.bind(this), { passive: false });
}
_handleTouchStart(e) {
e.preventDefault();
this.startTime = Date.now();
// 记录所有起始触摸点
this.startPoints = Array.from(e.touches).map(touch => ({
x: touch.clientX,
y: touch.clientY,
identifier: touch.identifier
}));
}
_handleTouchMove(e) {
e.preventDefault();
}
_handleTouchEnd(e) {
e.preventDefault();
const endTime = Date.now();
const duration = endTime - this.startTime;
const endPoints = Array.from(e.changedTouches).map(touch => ({
x: touch.clientX,
y: touch.clientY,
identifier: touch.identifier
}));
// 单指操作判断点击和滑动
if (this.startPoints.length === 1 && endPoints.length === 1) {
const start = this.startPoints[0];
const end = endPoints[0];
const deltaX = end.x - start.x;
const deltaY = end.y - start.y;
const distance = Math.sqrt(deltaX * deltaX + deltaY * deltaY);
// 判断点击
if (distance < this.tapMaxDistance && duration < this.tapMaxDuration) {
this.onTap && this.onTap({ x: end.x, y: end.y });
return;
}
// 判断滑动
if (distance >= this.swipeMinDistance) {
let direction;
if (Math.abs(deltaX) > Math.abs(deltaY)) {
direction = deltaX > 0 ? 'right' : 'left';
} else {
direction = deltaY > 0 ? 'down' : 'up';
}
this.onSwipe && this.onSwipe({
direction,
deltaX,
deltaY,
distance
});
}
}
// 双指缩放判断
if (this.startPoints.length === 2 && endPoints.length === 2) {
// 找到对应的两个触摸点
const startP1 = this.startPoints[0];
const startP2 = this.startPoints[1];
const endP1 = endPoints.find(p => p.identifier === startP1.identifier);
const endP2 = endPoints.find(p => p.identifier === startP2.identifier);
if (endP1 && endP2) {
// 计算起始距离和结束距离
const startDist = Math.sqrt(
Math.pow(startP2.x - startP1.x, 2) + Math.pow(startP2.y - startP1.y, 2)
);
const endDist = Math.sqrt(
Math.pow(endP2.x - endP1.x, 2) + Math.pow(endP2.y - endP1.y, 2)
);
const scale = endDist / startDist;
this.onPinch && this.onPinch({ scale, startDist, endDist });
}
}
}
}
// 使用示例
const target = document.getElementById('gesture-target');
const recognizer = new GestureRecognizer(target);
recognizer.onTap = (point) => {
console.log('点击位置:', point.x, point.y);
};
recognizer.onSwipe = (info) => {
console.log('滑动方向:', info.direction, '偏移量:', info.deltaX, info.deltaY);
};
recognizer.onPinch = (info) => {
console.log('缩放比例:', info.scale);
};开发注意事项
- 记得在事件监听时设置
passive: false,否则preventDefault可能无法生效,导致页面滚动干扰手势判断 - 多指操作时要注意通过
identifier匹配起始和结束的触摸点,避免位置对应错误 - 实际项目中可以根据需求调整阈值,比如滑动阈值可以根据屏幕尺寸动态计算,适配不同设备
- 如果需要支持更多手势比如长按、旋转,可以在现有基础上扩展判断逻辑,长按只需要判断触摸持续时间超过阈值且位置偏移很小即可
JavaScript手势识别TouchEvent手势算法前端交互修改时间:2026-05-29 23:24:25