boxmoe_header_banner_img

Hello! 欢迎来到悠悠畅享网!

文章导读

掌握JavaScript游戏触控事件:从键盘到触摸的平滑过渡


avatar
作者 2025年9月8日 10

掌握JavaScript游戏触控事件:从键盘到触摸的平滑过渡

本文旨在解决JavaScript游戏中将键盘事件转换为触控事件时常见的错误。通过分析e.code属性在不同事件类型中的适用性,我们将深入探讨touchstart事件的正确处理方式,并提供示例代码和最佳实践,确保您的游戏在移动设备上提供无缝的交互体验。

理解事件类型与事件对象属性

在开发javascript游戏时,将原本基于键盘输入的交互逻辑适配到触控设备是一个常见需求。然而,许多开发者在尝试将keypress或keydown事件直接替换为touchstart时,会遇到意料之外的问题。这通常源于对不同事件类型所附带的事件对象(Event Object)属性的误解。

以Flappy Bird为例,当我们需要将小鸟的跳跃动作从按下空格键(Space)改为触摸屏幕时,直观地将document.addEventListener(“keypress”, handleJump)改为document.addEventListener(“touchstart”, handleJump),并将handleJump函数中的条件判断从e.code !== “Space”改为e.code !== “touchstart”,看似合理,实则不然。

问题的核心在于e.code属性。e.code是KeyboardEvent(键盘事件)对象特有的属性,用于表示触发事件的物理按键代码,例如”Space”、”KeyA”等。而touchstart事件属于TouchEvent(触控事件)类型,其事件对象不包含code属性。因此,当touchstart事件触发时,e.code将是undefined。将undefined !== “touchstart”作为条件判断,实际上是试图比较一个不存在的属性与一个字符串,这会导致条件始终为真(或在严格模式下抛出错误),从而阻止了后续逻辑的执行。

正确处理触控事件

对于touchstart这类触控事件,我们通常不需要检查特定的“代码”来判断事件的来源,因为事件本身就代表了用户在屏幕上的触摸行为。如果事件监听器已经明确绑定到touchstart事件,那么当handleJump函数被调用时,我们就可以确信这是一个触摸事件。

因此,解决问题的关键在于移除或修改不适用于触控事件的条件判断。

立即学习Java免费学习笔记(深入)”;

原始(错误)的 handleJump 函数:

function handleJump(e) {   if (e.code !== "touchstart") return; // 错误:e.code 不适用于 touchstart 事件    timeSinceLastJump = 0; }

修正后的 handleJump 函数:

掌握JavaScript游戏触控事件:从键盘到触摸的平滑过渡

Copilot

Copilot是由微软公司开发的一款AI生产力工具,旨在通过先进的人工智能技术,帮助用户快速完成各种任务,提升工作效率。

掌握JavaScript游戏触控事件:从键盘到触摸的平滑过渡63

查看详情 掌握JavaScript游戏触控事件:从键盘到触摸的平滑过渡

function handleJump(e) {   // 对于 touchstart 事件,无需检查 e.code   // 如果需要防止默认的滚动/缩放行为,可以添加 e.preventDefault();   // e.preventDefault();     timeSinceLastJump = 0; }

通过移除if (e.code !== “touchstart”) return;这一行,handleJump函数在touchstart事件触发时将直接执行timeSinceLastJump = 0;,从而正确地触发小鸟的跳跃动作。

示例代码:Flappy Bird 触控适配

以下是基于原始代码进行修改的示例,展示了如何正确地将Flappy Bird游戏适配为触控友好型。

f.js (主页面逻辑):

import { updateBird, setupBird, getBirdRect } from "./Bird.JS"; import {   updatePipes,   setupPipes,   getPassedPipesCount,   getPipesRects, } from "./Pipe.js";  // 监听 touchstart 事件来启动游戏 document.addEventListener("touchstart", handleStart, { once: true }); const title = document.querySelector("[data-title]"); const subtitle = document.querySelector("[data-subtitle]");  let lastTime; function updateLoop(time) {   if (lastTime == null) {     lastTime = time;     window.requestAnimationFrame(updateLoop);     return;   }   const delta = time - lastTime;   updateBird(delta);   updatePipes(delta);   if (checkLose()) return handleLose();   lastTime = time;   window.requestAnimationFrame(updateLoop); }  function checkLose() {   const birdRect = getBirdRect();   const insidePipe = getPipesRects().some((rect) =>     isCollision(birdRect, rect)   );   const outsideWorld = birdRect.top < 0 || birdRect.bottom > window.innerHeight;   return outsideWorld || insidePipe; }  function isCollision(rect1, rect2) {   return (     rect1.left < rect2.right &&     rect1.top < rect2.bottom &&     rect1.right > rect2.left &&     rect1.bottom > rect2.top   ); }  function handleStart() {   title.classlist.add("hide");   setupBird();   setupPipes();   lastTime = null;   window.requestAnimationFrame(updateLoop); }  function handleLose() {   setTimeout(() => {     title.classList.remove("hide");     subtitle.classList.remove("hide");     subtitle.textContent = `${getPassedPipesCount()} Pipes`;     // 游戏结束后,再次监听 touchstart 来重新开始游戏     document.addEventListener("touchstart", handleStart, { once: true });   }, 100); }

Bird.js (小鸟逻辑):

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 的错误判断   // 建议:对于游戏中的触控事件,通常需要阻止默认行为以防止页面滚动或缩放   e.preventDefault();    timeSinceLastJump = 0; }

注意事项与最佳实践

  1. e.preventDefault(): 在处理触控事件时,尤其是在游戏中,强烈建议调用e.preventDefault()。这可以阻止浏览器执行与触摸相关的默认行为,例如页面滚动、缩放或触发上下文菜单。如果您的游戏不阻止这些默认行为,用户可能会在尝试玩游戏时意外地滚动页面。
  2. 事件监听器的管理: 在setupBird函数中,先removeEventListener再addEventListener是一个好习惯,可以防止重复添加相同的事件监听器,尤其是在游戏重置或重新开始时。
  3. click事件与touchstart/touchend:
    • touchstart:当用户触摸屏幕时立即触发。适合需要即时响应的动作(如跳跃、射击)。
    • touchend:当用户从屏幕上抬起手指时触发。适合需要释放动作的场景。
    • click:在touchstart和touchend之后,如果触摸没有移动且持续时间短,浏览器会模拟触发click事件。对于简单的“点击”交互,click事件可能更方便,因为它已经处理了触摸和鼠标事件的兼容性。但对于需要精确控制触摸生命周期的游戏,touchstart和touchend更合适。
  4. 多点触控: 如果您的游戏需要支持多点触控(例如,同时控制多个角色或执行组合操作),您需要利用TouchEvent对象的e.touches、e.targetTouches和e.changedTouches属性来获取当前所有触摸点的信息。
  5. 性能优化 频繁触发的触控事件(如touchmove)可能会影响性能。如果不需要实时追踪触摸点的移动,尽量避免在这些事件上执行复杂的计算。

总结

JavaScript游戏从键盘控制转换为触控控制时,理解不同事件类型(如KeyboardEvent和TouchEvent)的事件对象属性至关重要。e.code是键盘事件特有的属性,不适用于触控事件。对于touchstart事件,我们只需监听事件并执行相应的逻辑,同时考虑使用e.preventDefault()来优化用户体验。通过遵循这些原则和最佳实践,您可以确保您的JavaScript游戏在移动设备上提供流畅、响应迅速的触控交互。



评论(已关闭)

评论已关闭