用键盘事件构建你自己的浏览器小游戏:Web-Dev-For-Beginners 打字游戏章节实战作业全解析
导读
本篇文章围绕 Web-Dev-For-Beginners 课程第 4 单元「打字游戏(Typing Game)」的章节作业 Create a new keyboard game(作业原文见 assignment.md,阿拉伯语译文见 translations/ar/4-typing-game/typing-game/assignment.md)展开。该作业要求学习者在完成课堂打字游戏、掌握事件驱动编程之后,独立设计并实现一款"以键盘事件为核心玩法"的新游戏,从而综合检验事件监听、DOM 操作、应用状态与用户体验设计四项能力。读完本文,你将完整理解作业的每一项硬性要求与评分标准,并能够对照仓库内的课堂文档与完整可运行方案梳理出从需求拆解、代码结构到自评测试的落地路径。
作业背景:打字游戏课之后,为什么还要造一个"键盘游戏"
在完成这道作业之前,学习者会先在第 4 单元中走完一课完整的"事件驱动编程"训练。该单元入口文档 4-typing-game/README.md 明确指出,本模块的终极目标是用事件驱动编程构建一个实时追踪输入速度与准确率的打字游戏;其核心教案 typing-game/README.md 通过"浏览器把每一次点击、输入、鼠标移动都作为一条消息(事件)发给你的 JavaScript 代码"这一模型,讲解了事件监听器的注册方式,并带领学习者完成了一款以夏洛克·福尔摩斯名句为素材的打字练习游戏。
作业则把训练推进一步:不再满足于"跟随教程敲一遍代码",而是要求学习者基于对事件监听、DOM 交互与用户交互模式的理解,原创一款键盘驱动的小游戏。它既可以是一款新式的打字游戏,也可以是基于按键在屏幕上"作画"的画布应用、用方向键控制的简单街机游戏,或任何其他有创意的玩法。课堂打字游戏的运行界面(点击 Start 后开始输入、逐词高亮、错误即时反馈)就是本题最直接的参考基准,如下图所示:
作业硬性要求拆解:四项能力必须全部覆盖
作业正文给出了一张必须逐项满足的需求表,四项要求分别指向四种核心技术能力。下表为作业原文要求的完整保留:
| 要求 | 描述 | 目的 |
|---|---|---|
| 事件监听器 | 至少响应 3 种不同的键盘事件 | 展示对事件处理的理解 |
| 视觉反馈 | 对用户输入提供即时的视觉响应 | 展示对 DOM 操作的掌握 |
| 游戏逻辑 | 包含计分、关卡或进度机制 | 练习实现应用状态 |
| 用户界面 | 清晰的说明与直观易用的操控 | 培养用户体验设计能力 |
要求一:事件监听器——至少响应 3 种不同键盘事件
"事件驱动"是整道题的灵魂。课堂打字游戏在 solution/index.js 中只注册了两个监听器,分别监听一次 click(点击 Start)与一次 input(输入框内容变化):
// 事件监听器(来源:4-typing-game/solution/index.js)
startButton.addEventListener("click", startGame);
typedValueElement.addEventListener("input", handleTyping);
而作业要求的是至少 3 种键盘事件,这意味着新游戏需要把关注点从"文本框内容变化"迁移到"原始按键本身",典型组合包括:
keydown:按键被按下时触发(按下并保持会连续触发),适合处理移动、跳跃等"按住不放"的玩法;keyup:按键抬起时触发,适合实现"松开即结束/还原"的状态,例如松开方向键停止移动;keypress:字符键按下触发,常用于文本输入类玩法(需注意它与keydown的差异)。
事件对象是理解这套机制的钥匙。课堂教案在 typing-game/README.md 中给出了 click、contextmenu、select、input 等常用事件的最小清单;对于键盘驱动的新游戏,你更常用到的是键盘事件对象的属性:e.key(返回按键的字符或名称,如 "ArrowUp"、" "、"a")、e.code(返回物理键位,如 "KeyA"、"Space"),以及 e.ctrlKey、e.shiftKey、e.altKey 等修饰键布尔值。将"不同按键"映射到"不同行为",正是作业要求的核心演示点。
要求二:视觉反馈——用 DOM 操作实现"所见即所得"
课堂打字游戏对"即时反馈"的演示极具代表性。其样式层在 solution/index.css 中定义了两个状态类:
.highlight {
background-color: yellow;
}
.error {
background-color: lightcoral;
border-color: red;
}
逻辑层则借助 classList 在事件回调里动态切换这些类:solution/index.js 的 highlightWord() 用 classList.toggle("highlight", i === index) 把"应当输入的当前单词"高亮为黄色;solution/index.js 的 handleTyping() 则在输入正确前缀时 classList.remove("error")、出现错误时 classList.add("error") 把输入框染成浅珊瑚色。
你的新游戏可以沿用同一模式:任何一次按键,都要让玩家在同一帧内看到"发生了什么"。例如格斗或躲避类游戏用 classList 切换受伤/护盾状态、像素画板用改变目标格子背景色代表"此键已作画",这都属于对 DOM 操作的即时响应。作业评分中"视觉反馈"与"DOM 操作熟练度"直接挂钩,反馈越即时、越有区分度,得分越高。
要求三:游戏逻辑——计分、关卡或进度机制中的"应用状态"
课堂实现把"应用状态"集中管理在三个 let 变量中(见 solution/index.js):words(当前句子拆成的单词数组)、wordIndex(玩家当前正输入到第几个单词)、startTime(本局开始时间戳)。每一局开始时 startGame() 都会重置这些状态并刷新计时(solution/index.js),随后由 handleTyping() 的四分支判断推动状态前进(solution/index.js):
- 输入完全等于最后一个单词且处于末词 → 本局结束,用
(Date.now() - startTime) / 1000计算用时并展示; - 输入以空格结尾且去除空格后完全匹配当前词 → 清空输入框、
wordIndex++、把高亮推进到下一个词; - 当前单词以已输入内容开头 → 输入正确,清除错误样式;
- 以上都不满足 → 进入错误态,打上
error样式并提示。
作业要求的新游戏必须包含计分、关卡或进度中的至少一种机制,本质上就是要求你为自己的游戏显式设计一套"状态机":分数、生命、等级、连击、已用时间等都应像 wordIndex 一样,由清晰命名的状态变量承载,并在事件回调中单调、可预测地推进,最终把状态渲染回 DOM。
要求四:用户界面——说明清晰、操控直观、可访问
课堂游戏的 HTML(见 solution/index.html)是 UI 设计的迷你范本:一句"点击 Start 显示引号,然后尽快打出整句"的指令、一个 #quote 展示区、一个 #message 状态区、带 aria-label="current word" 的文本框 #typed-value 以及 Start 按钮。为了兼顾可访问性,它甚至为输入框隐藏标签提供了专门的 .hidden-label 样式(见 solution/index.html),让屏幕阅读器用户也能理解输入框的用途。
作业要求你在新游戏中做到同样的事情:页面上必须写清楚玩法与按键说明,让第一次打开游戏的玩家不用猜;每个控件要语义清晰;要合理使用 focus()(课堂里在开局时把焦点移到输入框,见 solution/index.js)让玩家开局即输,无需先点一下输入区域。
可选创意方向:从想法到玩法机制
作业列出了七个可供参考的创意方向,全部围绕"同一组键盘事件映射出差异化行为"这一主旨。下表完整保留作业原文的创意清单,并在每项后补充其典型的事件/状态实现思路:
| 创意方向 | 玩法描述 | 可落地的实现要点 |
|---|---|---|
| 节奏游戏(Rhythm Game) | 玩家配合音乐或视觉提示按时按键 | keydown 判断按键时间与判定窗口的差值,用 score/连击数记录精度 |
| 像素画创作器(Pixel Art Creator) | 不同按键画出不同颜色或图案 | 用 e.key 建立"按键 → 颜色"映射表,按键即给当前格子上色(对应 setAttribute/style 操作) |
| 单词构建(Word Builder) | 玩家按特定顺序键入字母拼出单词 | 与课堂 wordIndex 思路一致:维护 expectedSequence 与 currentIndex 状态,逐字母校验 |
| 贪吃蛇(Snake Game) | 用方向键控制蛇收集物品 | keydown 只改写"方向"状态,再由定时器/帧循环推动蛇身移动,避免一帧多动 |
| 音乐合成器(Music Synthesizer) | 不同按键发出不同音符或声音 | keydown/keyup 分别对应发声与停声,用 Web Audio 振荡器频率映射键位 |
| 打字变体(Speed Typing Variants) | 按指定分类输入(编程术语、外语单词等) | 更换 quotes 数据源为分类词库,并扩展计分规则(如术语正确率加成) |
| 键盘鼓手(Keyboard Drummer) | 将不同按键映射到不同鼓点声 | 类似合成器,但用采样音色 + 记录"节拍正确度"充当计分机制 |
实施路径:从简单原型开始的五条准则
作业在"实施指南"部分给出了五条软性建议,它们决定了你的代码能否从"能跑"进化到"优秀"。这里逐条结合仓库实现给出可操作的注解:
- 从一个简单的想法开始,逐步增加复杂度。 课堂教案的代码组织方式是极好的示范:先做"常量与 DOM 引用"(solution/index.js),再写启动逻辑,最后补输入逻辑。新游戏同样应先把"最小可玩闭环"(按下某键 → 屏幕上发生某件事)跑通,再追加计分、关卡与音效。
- 把操控的流畅与响应性放在首位。 事件回调必须保持轻量:不要在
keydown里做重计算或高频 DOM 全量重绘;像课堂那样只做"类切换/文本更新"级别的操作即可保证手感自然。 - 提供清晰可见的游戏状态与进度指示。 参考课堂对"当前单词高亮"与"消息区提示"的双通道设计:让玩家在任何时刻都能同时知道"我现在该做什么"(指令)和"我做到哪了"(进度)。
- 找不同用户试玩以保证玩法直觉。 作业强调"不同用户测试"——因为在事件交互中,你觉得显而易见的按键习惯,别人未必能立刻领会;试玩记录可以直接转化为 UI 文案的改进点。
- 用注释记录你的事件处理策略。 命名清晰、职责单一的函数(如课堂的
startGame、handleTyping、renderQuote、highlightWord,见 solution/index.js)本身就是"文档",再辅以解释"为何把某个监听器挂在document而非某个元素"之类的注释,能让评分者一眼看出你的设计意图。
从课堂代码到新游戏的工程建议:三种可直接迁移的模式
仓库中的 4-typing-game/solution/ 提供了完整的参考实现,其目录结构(index.html + index.js + index.css)与教案要求的"三件套"一致。从课堂代码迁移到你的新键盘游戏,有三个模式值得直接复用:
模式一:命名函数 + 尾部注册监听器。 课堂把 startGame、handleTyping 定义为具名函数后在文件末尾统一 addEventListener。好处是事件回调可读、可复用,也便于在"开始/结束"时动态增删监听器(教案挑战环节提到的"完成后禁用输入事件、点 Start 再启用"正是基于这一结构)。
模式二:一局游戏的完整状态重置。 startGame() 中"重置 wordIndex → 清空输入框 → 重新渲染 → focus() → 记录 startTime"的顺序几乎可以原样套用到任何单局制游戏(分数归零 → 清空画布/场地 → 渲染新内容 → 聚焦/等待首个按键 → 计时开始)。
模式三:键盘事件的标准姿势。 对新游戏而言,监听器通常应挂在 document 上而非某个输入框,因为按键行为不局限于一个控件。下面是一个可作起点的"键盘画板"式骨架(依据上述模式撰写,并非仓库自带代码,仅供对照作业要求练习):
// 思路骨架:按键 → 行为 → 状态 → 反馈(示意代码)
const keyActions = {
"ArrowUp": "up", "ArrowDown": "down",
"ArrowLeft": "left", "ArrowRight": "right",
" ": "fire",
};
let score = 0; // 游戏逻辑:计分状态
const scoreEl = document.querySelector("#score");
document.addEventListener("keydown", (e) => {
const action = keyActions[e.key]; // 第 1 种键盘事件:keydown
if (!action) return; // 过滤无关按键
e.preventDefault(); // 阻止空格等默认滚动行为
updatePlayer(action); // 更新游戏状态(DOM 即时反馈)
scoreEl.textContent = ++score; // 计分状态落回 DOM
});
document.addEventListener("keyup", (e) => {
// 第 2 种键盘事件:keyup——松开方向键停止移动、松开发射键结束蓄力
stopAction(keyActions[e.key]);
});
实现时请记住课堂代码的"瀑布式分支"思路:判断顺序从最具体的完成态 → 次具体的推进态 → 通用正确态 → 错误态,这能让逻辑覆盖所有边界而不错乱。
用评分标准自评:四个维度决定"优秀"与"合格"的差距
作业自带一张四维度评分表(Rubric)。把它当作自测清单,而不是交稿后才看的说明,往往比任何调试都更有效地提升最终分数。下表完整保留作业评分标准:
| 标准 | 优秀 | 合格 | 需要改进 |
|---|---|---|---|
| 功能 | 完整且打磨过的游戏,包含多个特性与流畅的玩法 | 功能正常、具备基础特性并能演示键盘事件处理的游戏 | 实现过于简单、功能有限或存在明显缺陷 |
| 代码质量 | 组织良好、注释完善、遵循最佳实践,事件处理高效 | 干净可读,恰当使用事件监听器与 DOM 操作 | 基础代码结构,存在组织问题或低效实现 |
| 用户体验 | 操控直觉、反馈清晰、玩法有吸引力、观感专业 | 界面可用,具备足够的引导与响应式操控 | 界面简陋、说明含糊或响应性差 |
| 创造力 | 原创概念,对键盘事件有创新运用与创造性问题求解 | 对常见玩法做出有趣变体,事件处理运用得当 | 对基础概念做简单实现,创意元素寥寥 |
结合课堂代码可以把"优秀"档翻译成可执行清单:
- 功能维度:是否实现了完整的"开始 → 游玩 → 结算 → 再来一局"闭环?键盘事件是否覆盖了 3 种以上?计分/关卡/进度是否在结束条件上正确收束(对照 solution/index.js 中完成判定后禁用输入框的收尾处理)?
- 代码质量:状态变量是否都
let化并集中在顶部?DOM 引用是否用常量缓存避免重复querySelector?事件回调是否拆成具名小函数?关键分支是否有注释说明事件处理策略? - 用户体验:页面上是否有开局即见的玩法说明?错误时除了颜色是否还有文字提示(课堂的
messageElement.textContent = messages.error即为一例)?键盘焦点管理是否正确? - 创造力:玩法是否不是课堂打字游戏的简单换皮?按键与行为的映射是否形成了有辨识度的玩法特色?
测试与调试:交作业前必须过一遍的检查单
课堂教案在"测试你的应用"一节给出了面向打字游戏的四条常见排错经验,同样适用于任何键盘游戏,现结合本作业要求转写为最终检查单:
- 每个需求点单独验证:分别确认 3 种键盘事件都能触发、视觉反馈在"正确/错误/中立"三态下都可见、计分/关卡/进度在结束条件处正确结算。
- 打开浏览器控制台(DevTools,F12)观察 JavaScript 报错——键盘事件回调一旦抛异常,通常表现为"按键无反应"而非崩溃,所以请重点在游戏中连续快速按键、按住不松,观察是否有异常或状态错乱。
- 核对文件命名与大小写:HTML 中引用的
script.js/style.css或仓库方案里的index.js/index.css必须与实际文件名完全一致,否则 Live Server 页面会静默空白。 - 验证"重复开局"路径:同一局结束后能否干净地重新开始?参考课堂
startGame()中清空输入框、清除旧消息、重置wordIndex与startTime的顺序,确认没有上一局的"残留状态"漏网。 - 做可访问性与键盘遍历测试:尝试纯键盘操作完成整个流程,确认页面元素可用 Tab 聚焦、必要控件带
aria-label等无障碍属性。
完成之后的进阶方向
作业是"及格线",教案末尾的挑战与进阶列表则为想再进一步的你准备了自然延伸:用 localStorage 实现最高分持久化;加入基于 WPM(每分钟字数)与准确率的难度自适应系统;为"完成态/错误态"增加音效与动画;为不同语言与键盘布局扩展支持。这些延伸的共同点依旧是本作业反复锤炼的那条主线——用事件把用户动作、应用状态与 DOM 反馈三者可靠地串联起来。
仓库参考索引
atomcodeClaude Code 的开源替代方案。连接任意大模型,编辑代码,运行命令,自动验证 — 全自动执行。用 Rust 构建,极致性能。 | An open-source alternative to Claude Code. Connect any LLM, edit code, run commands, and verify changes — autonomously. Built in Rust for speed. Get StartedRust0624
Hy4-previewHy4 preview 是由腾讯混元团队研发的新一代混合专家(MoE)旗舰模型。模型总参数量 770B,每个 token 激活 49B,主干共包含78层,第一层采用标准 FFN,其余 77 层均为 MoE 结构,每层包含 256 个路由专家与 1 个共享专家,每个 token 激活 top-8 路由专家及共享专家。主干之外原生内置 1 层 MTP(总参数量 10B,激活 0.7B)以支持投机解码。Python00
GLM-5.3GLM-5.3 与 GLM-5.2 使用相同的基座模型——所有提升均来自后训练。与 GLM-5.2 相比,它在复杂编程和长程任务上的表现显著提升。Jinja00
GLM-5.3-FlashGLM-5.3-Flash (320B-A18B),是GLM-5系列的首个原生多模态模型。320B总参数,能力超过GLM-5.2Jinja00
Spark-X2.5-4BSpark-X2.5-4B 旨在让强大的 AI 更实用、更高效、更易获得。在广泛日常任务中表现强劲,涵盖对话、写作、翻译、推理、编码、工具调用以及智能体工作流,并在同等规模的开源模型中取得领先成绩。Spark-X2.5 将面向效率的架构与最高 1M tokens 的原生上下文窗口相结合,并支持 200 多种语言。Python00
Spark-X2.5-1.7BSpark-X2.5-1.7B 旨在让强大的 AI 更加实用、高效且易于获取。这些模型在广泛的日常任务中表现出色,涵盖对话、写作、翻译、推理、编程、工具调用和智能体工作流,并在同等规模的开源模型中取得领先结果。Spark-X2.5 将面向效率的架构与最高 1M tokens 的原生上下文窗口相结合,并支持 200 多种语言。Python00
