changedTouches属性在多点触控时如何区分?

萌新.梓晴 阅读 37

最近在做一个需要支持多点触控的手势操作的功能,用到了touch事件里的changedTouches。但是发现当两个手指同时移动时,changedTouches里面会同时包含两个触摸点的信息,有点分不清哪个是哪个了。

试着通过identifier来区分不同的触摸点,但有时候identifier的值会重复出现,搞不清楚到底该怎样准确地追踪每个手指的动作。

我来解答 赞 6 收藏
二维码
手机扫码查看
2 条解答
❤欢欢
❤欢欢 Lv1
这个问题其实挺常见的,很多人在处理多点触控时都会被 changedTouchesidentifier 搞晕。我来一步步解释怎么解决。

首先我们得搞清楚 changedTouches 是什么。它是一个 TouchList 对象,里面包含了当前事件中发生变化的触摸点信息。比如你两个手指同时移动,changedTouches 会记录这两个手指的变化。但问题来了,这里面的触摸点顺序可能每次都不一样,所以不能直接通过数组下标来区分手指。这时候就需要用到每个触摸点的 identifier 属性了。

identifier 是每个触摸点的唯一标识符,从触摸开始到触摸结束,这个值是不会变的。所以我们可以利用它来追踪每个手指的动作。这里需要注意的是,identifier 只在同一个触摸会话中是唯一的,一旦手指离开屏幕,这个标识符可能会被重新分配给新的触摸点。所以我们需要结合触摸事件的生命周期来管理这些标识符。

接下来我们分步骤实现一个多点触控的手势追踪功能:

第一步,我们需要维护一个对象来存储每个触摸点的信息。可以用一个普通的 JavaScript 对象,键就是 identifier,值是一个包含触摸点信息的对象。

第二步,在 touchstart 事件中,把新出现的触摸点加到这个对象里。因为只有新的触摸点才会触发 touchstart

第三步,在 touchmove 事件中,根据 changedTouches 里的 identifier 找到对应的触摸点,然后更新它的位置信息。

第四步,在 touchendtouchcancel 事件中,把结束的触摸点从对象里移除。

下面是一个完整的代码示例:

// 存储触摸点的对象
let touches = {};

// touchstart 事件处理函数
function handleTouchStart(event) {
// 遍历 touches 列表,添加新的触摸点
for (let i = 0; i < event.changedTouches.length; i++) {
let touch = event.changedTouches[i];
// 把触摸点存到对象里,键是 identifier
touches[touch.identifier] = {
identifier: touch.identifier,
pageX: touch.pageX,
pageY: touch.pageY
};
console.log(手指 ${touch.identifier} 开始触摸);
}
}

// touchmove 事件处理函数
function handleTouchMove(event) {
// 遍历 changedTouches 列表,更新触摸点的位置
for (let i = 0; i < event.changedTouches.length; i++) {
let touch = event.changedTouches[i];
if (touches[touch.identifier]) {
// 更新对应触摸点的位置
touches[touch.identifier].pageX = touch.pageX;
touches[touch.identifier].pageY = touch.pageY;
console.log(手指 ${touch.identifier} 移动到 (${touch.pageX}, ${touch.pageY}));
}
}
}

// touchend 事件处理函数
function handleTouchEnd(event) {
// 遍历 changedTouches 列表,移除结束的触摸点
for (let i = 0; i < event.changedTouches.length; i++) {
let touch = event.changedTouches[i];
if (touches[touch.identifier]) {
console.log(手指 ${touch.identifier} 结束触摸);
delete touches[touch.identifier];
}
}
}

// 绑定事件监听器
document.addEventListener('touchstart', handleTouchStart);
document.addEventListener('touchmove', handleTouchMove);
document.addEventListener('touchend', handleTouchEnd);
document.addEventListener('touchcancel', handleTouchEnd); // touchcancel 和 touchend 处理方式相同


这里有几个地方需要特别注意:

1. changedTouches 里的触摸点顺序不是固定的,所以不能依赖数组下标,一定要用 identifier 来区分。
2. touchendtouchcancel 事件也需要清理触摸点,否则触摸点对象会残留无效的数据。
3. 如果你需要实现复杂的手势操作,比如缩放或旋转,可以根据存储的触摸点计算两点之间的距离和角度变化。

总之,关键就在于用 identifier 做键,把每个触摸点的状态存起来,这样就能准确地追踪每个手指的动作了。希望这个方法能帮你解决问题!
点赞
2026-02-19 18:08
程序猿晨妍
changedTouches确实会把所有发生变化的触摸点都塞进去,这就会导致你提到的问题:多个手指同时动的时候不好区分。问题应该出在你没有正确利用identifier来持续追踪每个触摸点。

虽然identifier看起来可能会重复,但实际上它是每个触摸点的唯一标识符,在一次完整的触摸过程中(从按下到离开)是不会变的。你需要做的就是在touchstart的时候记录每个触摸点的identifier和它的初始位置,然后在touchmove里通过这个identifier去匹配对应的触摸点。

这里给你一个简单的例子:

let touchesMap = new Map();

document.addEventListener('touchstart', (e) => {
e.changedTouches.forEach(touch => {
touchesMap.set(touch.identifier, {
startX: touch.clientX,
startY: touch.clientY
});
});
});

document.addEventListener('touchmove', (e) => {
e.changedTouches.forEach(touch => {
const touchInfo = touchesMap.get(touch.identifier);
if (touchInfo) {
console.log(Touch ${touch.identifier}:,
Moved from (${touchInfo.startX}, ${touchInfo.startY}) to (${touch.clientX}, ${touch.clientY}));
}
});
});

document.addEventListener('touchend', (e) => {
e.changedTouches.forEach(touch => {
touchesMap.delete(touch.identifier);
});
});


这段代码的核心就是用Map来保存每个触摸点的信息,并且通过identifier作为key来进行追踪。这样无论多少个手指动,都能准确知道每个手指的具体动作了。

如果还有其他地方卡住,随时说,我再帮你看看!
点赞 14
2026-01-30 16:08