
1. 从键盘到触控:事件处理的转变
在开发Web游戏时,我们经常从桌面环境开始,使用键盘事件(如keydown或keypress)来控制游戏角色。例如,在一个类似Flappy Bird的游戏中,按下空格键让小鸟跳跃是一种常见的交互方式。然而,当我们将游戏移植到移动设备时,就需要将这些键盘事件替换为触控事件,以适应触摸屏操作。最常用的触控事件包括touchstart(手指接触屏幕)、touchmove(手指在屏幕上移动)和touchend(手指离开屏幕)。
问题通常出现在将键盘事件的逻辑直接套用到触控事件上时,尤其是在事件对象的属性判断上。
2. 理解事件对象:KeyboardEvent与TouchEvent的区别
在JavaScript中,不同的事件类型会产生不同结构的事件对象。这是理解为何特定代码在触控事件中失效的关键。
- KeyboardEvent对象: 当用户按下或释放键盘按键时触发,例如keydown、keypress、keyup。这些事件的事件对象(e)包含code、key等属性,用于识别按下的具体键。例如,按下空格键时,e.code的值通常是"Space"。
- TouchEvent对象: 当用户与触摸屏交互时触发,例如touchstart、touchmove、touchend。这些事件的事件对象(e)不包含code或key这样的属性来标识“触摸键”。相反,它们提供了touches、targetTouches和changedTouches等属性,这些属性是TouchList对象,包含了所有当前与屏幕接触的手指信息(如坐标、ID等)。
3. 常见错误分析与解决方案
在将键盘事件keypress改为触控事件touchstart时,一个常见的错误是将键盘事件中的按键判断逻辑直接应用到触控事件中。
立即学习“Java免费学习笔记(深入)”;
考虑以下原始的handleJump函数:
function handleJump(e) {
// 错误:尝试使用e.code判断touchstart事件
if (e.code !== "touchstart") return;
timeSinceLastJump = 0;
}错误原因: 如前所述,touchstart事件的事件对象(e)并没有code属性,更不会有值为"touchstart"的code属性。因此,e.code在touchstart事件中通常是undefined。undefined !== "touchstart"这个条件永远为true,导致return语句被执行,timeSinceLastJump = 0;这行代码永远不会被触发,小鸟自然也无法跳跃。
解决方案: 对于touchstart事件,我们通常不需要检查特定的“键码”,因为只要事件被触发,就意味着用户进行了触摸操作。因此,可以直接执行相应的逻辑,或者如果需要区分多点触控,则检查e.touches数组的长度。
正确的handleJump函数应该移除对e.code的错误判断:
function handleJump(e) {
// 正确:直接执行跳跃逻辑,因为touchstart事件本身就代表了触摸动作
timeSinceLastJump = 0;
// 可选:阻止默认行为,例如滚动或缩放
// e.preventDefault();
}4. 完整的代码示例(Bird.js)
下面是修改后的Bird.js文件,展示了如何正确地设置和处理touchstart事件:
const birdElem = document.querySelector("[data-bird");
const BIRD_SPEED = 0.5;
const JUMP_DURATION = 120;
let timeSinceLastJump = Number.POSITIVE_INFINITY;
export function setupBird() {
setTop(window.innerHeight / 2);
// 确保移除旧的事件监听器,避免重复绑定
document.removeEventListener("touchstart", handleJump);
// 绑定touchstart事件
document.addEventListener("touchstart", handleJump);
}
export function updateBird(delta) {
if (timeSinceLastJump < JUMP_DURATION) {
setTop(getTop() - BIRD_SPEED * delta);
} else {
setTop(getTop() + BIRD_SPEED * delta);
}
timeSinceLastJump += delta;
}
export function getBirdRect() {
return birdElem.getBoundingClientRect();
}
function setTop(top) {
birdElem.style.setProperty("--bird-top", top);
}
function getTop() {
return parseFloat(getComputedStyle(birdElem).getPropertyValue("--bird-top"));
}
function handleJump(e) {
// 移除对e.code的错误判断
timeSinceLastJump = 0;
// 在某些情况下,你可能需要阻止触摸事件的默认行为,
// 例如防止页面滚动或缩放,但这取决于游戏需求。
// e.preventDefault();
}同时,在主页面(f.js)中,确保游戏启动和重新开始的逻辑也正确使用了touchstart事件:
// f.js 部分代码
// document.addEventListener("keypress", handleStart, { once: true }); // 旧的键盘事件
document.addEventListener("touchstart", handleStart, { once: true }); // 新的触控事件
// ...
function handleLose() {
setTimeout(() => {
// ...
// document.addEventListener("keypress", handleStart, { once: true }); // 旧的键盘事件
document.addEventListener("touchstart", handleStart, { once: true }); // 新的触控事件
}, 100);
}5. 触控交互的最佳实践
- 阻止默认行为: 对于游戏中的触控操作,通常建议使用e.preventDefault()来阻止浏览器对触摸事件的默认处理,例如页面滚动、缩放或长按菜单。这可以确保游戏获得完整的控制权,并提供更沉浸的体验。但请注意,过度使用可能会影响辅助功能。
- 区分单点与多点触控: 如果游戏需要支持多点触控(例如,两根手指进行不同操作),可以通过检查e.touches.length来判断当前屏幕上的触摸点数量。
- 响应区域设计: 确保游戏中的可交互元素(如按钮、操作区域)有足够大的触控响应区域,以方便用户操作,避免误触。
- 性能优化: touchmove事件可能会非常频繁地触发,如果其处理函数中包含复杂的计算或DOM操作,可能会影响性能。考虑使用节流(throttle)或防抖(debounce)技术来限制touchmove事件处理的频率。
- 测试: 在实际移动设备上进行充分测试至关重要,因为模拟器可能无法完全复现真实的触控行为和性能表现。
总结
将JavaScript游戏从键盘控制转换为触控操作,核心在于正确理解和处理不同事件类型的事件对象。对于touchstart事件,我们应避免使用e.code等键盘事件特有的属性进行判断。通过直接响应touchstart事件并根据需要阻止默认行为,我们可以轻松地为游戏添加触控功能,从而在移动设备上提供更自然、更友好的用户体验。遵循触控交互的最佳实践,将有助于构建高性能且用户满意的触控游戏。











