TouchEvent开发问题:多指捏合时如何判断抬起的手指索引?
触摸手势中处理部分手指抬起的解决方案
核心原理
每个Touch对象自带唯一的identifier属性,从触摸开始到结束(或取消)全程不变,这是识别具体触摸点的关键。另外,TouchList是只读类数组对象,无法直接使用数组方法(如splice),所以需要将其转换为普通数组缓存。
双指场景修正代码
// 改为数组类型存储,方便修改 let cachedStartTouches: Touch[] = []; let cachedMoveTouches: Touch[] = []; function onStart(ev: TouchEvent) { // 将TouchList转为数组缓存 cachedStartTouches = Array.from(ev.touches); } function onMove(ev: TouchEvent) { // 更新缓存时,按identifier匹配对应触摸点,而非直接替换整个数组 const currentTouches = Array.from(ev.touches); cachedMoveTouches = cachedMoveTouches.map(cachedTouch => { const match = currentTouches.find(t => t.identifier === cachedTouch.identifier); return match || cachedTouch; }); // 补充新加入的触摸点(适配多指逐步按下的场景) const newTouches = currentTouches.filter(t => !cachedMoveTouches.some(c => c.identifier === t.identifier)); cachedMoveTouches.push(...newTouches); } function onEnd(ev: TouchEvent) { // 获取本次结束的触摸点集合 const endedTouches = Array.from(ev.changedTouches); // 逐个移除缓存中对应的触摸点 endedTouches.forEach(endedTouch => { // 找到缓存中匹配identifier的索引 const startIdx = cachedStartTouches.findIndex(t => t.identifier === endedTouch.identifier); const moveIdx = cachedMoveTouches.findIndex(t => t.identifier === endedTouch.identifier); if (startIdx !== -1) cachedStartTouches.splice(startIdx, 1); if (moveIdx !== -1) cachedMoveTouches.splice(moveIdx, 1); }); // 剩余触摸点数量:cachedStartTouches.length }
N指捏合的通用处理逻辑
初始化与缓存:
- 触摸开始时,将所有
Touch转为数组缓存,同时记录每个触摸点的初始位置、时间等手势所需基础数据。 - 建议维护自定义数据结构(而非直接存
Touch对象),避免浏览器内部对象的潜在问题,示例结构参考下方代码。
- 触摸开始时,将所有
触摸移动更新:
- 每次移动事件触发时,用当前
ev.touches中的identifier匹配缓存项,更新对应触摸点的实时数据。 - 适配逐步按下的多指场景:如果当前触摸点存在缓存中没有的
identifier,将其加入缓存并记录初始数据。
- 每次移动事件触发时,用当前
手指抬起/取消处理:
- 同时监听
touchEnd和touchCancel事件,通过ev.changedTouches获取本次结束的所有触摸点。 - 遍历这些触摸点,用
identifier在缓存数组中找到对应项并移除。 - 根据剩余触摸点数量决定后续逻辑:比如N指捏合变为N-1指时,调整缩放/旋转计算逻辑;若剩余触摸点不足,则结束当前手势。
- 同时监听
自定义缓存结构示例
// 自定义缓存结构,存储手势所需核心数据 interface TouchGestureData { identifier: number; startX: number; startY: number; currentX: number; currentY: number; } let gestureTouches: TouchGestureData[] = []; function onStart(ev: TouchEvent) { Array.from(ev.touches).forEach(touch => { const existing = gestureTouches.find(g => g.identifier === touch.identifier); if (!existing) { gestureTouches.push({ identifier: touch.identifier, startX: touch.clientX, startY: touch.clientY, currentX: touch.clientX, currentY: touch.clientY }); } }); } function onMove(ev: TouchEvent) { Array.from(ev.touches).forEach(touch => { const gestureData = gestureTouches.find(g => g.identifier === touch.identifier); if (gestureData) { gestureData.currentX = touch.clientX; gestureData.currentY = touch.clientY; } }); // 执行N指手势计算(如缩放比例、旋转角度) calculateMultiFingerGesture(gestureTouches); } function onEndOrCancel(ev: TouchEvent) { const changedIds = Array.from(ev.changedTouches).map(t => t.identifier); gestureTouches = gestureTouches.filter(g => !changedIds.includes(g.identifier)); // 如果剩余触摸点不足,结束手势;否则继续 if (gestureTouches.length < 2) { endGesture(); } } function calculateMultiFingerGesture(touches: TouchGestureData[]) { // 实现N指缩放、旋转等逻辑 if (touches.length >= 2) { // 计算两点距离、中心等 } }
内容的提问来源于stack exchange,提问作者eczn
相关产品推荐
相关产品推荐

