移动端双Joystick独立触摸事件异常修复求助
双摇杆触摸事件冲突的修复方案
问题根源
你的代码中所有触摸移动事件都固定取e.touches[0],当同时触摸两个摇杆时,两个Joystick实例都会响应同一个触摸点,导致第二个摇杆复制第一个的轨迹。移动端触摸事件支持多点触摸,每个触摸点有唯一的identifier,必须通过这个ID来绑定摇杆和对应的触摸操作。
修复后的完整代码
class Joystick { constructor(joystickElement, stickElement, stickNumber) { this.joystickElement = joystickElement; this.stickElement = stickElement; this.isActive = false; this.touchIdentifier = null; // 新增:存储当前触摸点的唯一ID this.initialPositionX = 0; this.initialPositionY = 0; this.cursorOffsetX = 0; this.cursorOffsetY = 0; this.stickNumber = stickNumber; stickElement.addEventListener('touchstart', (e) => this.onTouchStart(e)); document.addEventListener('touchmove', (e) => this.onTouchMove(e)); document.addEventListener('touchend', (e) => this.onTouchEnd(e)); document.addEventListener('touchcancel', (e) => this.onTouchEnd(e)); // 补充处理触摸取消事件 } onTouchStart(e) { e.preventDefault(); this.isActive = true; // 记录当前触摸点的唯一ID this.touchIdentifier = e.changedTouches[0].identifier; this.stickElement.style.transition = '0s'; const joystickRect = this.joystickElement.getBoundingClientRect(); const touch = e.changedTouches[0]; this.initialPositionX = touch.clientX - joystickRect.left - this.stickElement.offsetWidth / 2; this.initialPositionY = touch.clientY - joystickRect.top - this.stickElement.offsetHeight / 2; this.cursorOffsetX = this.stickElement.offsetWidth / 2; this.cursorOffsetY = this.stickElement.offsetHeight / 2; } onTouchMove(e) { if (this.isActive && this.touchIdentifier !== null) { // 找到当前摇杆对应的触摸点 const touch = Array.from(e.touches).find(t => t.identifier === this.touchIdentifier); if (!touch) return; // 找不到对应触摸点则退出 const joystickRect = this.joystickElement.getBoundingClientRect(); this.moveStick( touch, joystickRect, this.stickElement, this.cursorOffsetX, this.cursorOffsetY, this.initialPositionX, this.initialPositionY, this.stickNumber ); } } onTouchEnd(e) { // 只有当离开的触摸点是当前摇杆绑定的触摸点时,才重置状态 const endedTouch = Array.from(e.changedTouches).find(t => t.identifier === this.touchIdentifier); if (endedTouch) { this.isActive = false; this.touchIdentifier = null; this.stickElement.style.transition = '0.2s'; this.resetStickPosition(this.stickElement, this.stickNumber); } } moveStick(touch, joystick, stick, cursorOffsetX, cursorOffsetY, initialPositionX, initialPositionY, stickNumber) { const x = touch.clientX - joystick.left - cursorOffsetX - initialPositionX; const y = touch.clientY - joystick.top - cursorOffsetY - initialPositionY; const stickRadius = stick.offsetWidth / 2; const maxDistance = joystick.width / 2 - stickRadius + 30; const distance = Math.sqrt(x * x + y * y); if (distance <= maxDistance) { stick.style.transform = `translate(${x - 32}px, ${y - 32}px)`; } else { const angle = Math.atan2(y, x); const newX = Math.cos(angle) * maxDistance; const newY = Math.sin(angle) * maxDistance; stick.style.transform = `translate(${newX - 32}px, ${newY - 32}px)`; } switch (stickNumber) { case '1': document.getElementById('stick1x').textContent = x; document.getElementById('stick1y').textContent = y; break; case '2': document.getElementById('stick2x').textContent = x; document.getElementById('stick2y').textContent = y; break; } } resetStickPosition(stick, stickNumber) { stick.style.transform = 'translate(-50%, -50%)'; switch (stickNumber) { case '1': document.getElementById('stick1x').textContent = '0'; document.getElementById('stick1y').textContent = '0'; break; case '2': document.getElementById('stick2x').textContent = '0'; document.getElementById('stick2y').textContent = '0'; break; } } } const joystick1 = new Joystick(document.getElementById('joystick1'), document.getElementById('stick1'), '1'); const joystick2 = new Joystick(document.getElementById('joystick2'), document.getElementById('stick2'), '2');
关键修改点
- 新增
touchIdentifier属性,绑定当前摇杆对应的触摸点唯一ID touchstart时记录触摸点的identifier,而不是直接取第一个触摸点touchmove时遍历所有触摸点,找到与当前摇杆绑定的ID匹配的触摸点再处理touchend和touchcancel事件中,只有当离开的触摸点是当前摇杆绑定的ID时,才重置状态- 替换
innerHTML为textContent,避免不必要的HTML解析,提升性能
内容的提问来源于stack exchange,提问作者Anton Virovets
相关产品推荐
相关产品推荐

