从零构建打字游戏:技术路线、实战心得与优化思考
从零构建打字游戏:技术路线、实战心得与优化思考
作者:Eddygit
日期:2026-08-27
项目:typing-game
一、为什么做打字游戏
打字速度是开发者的基本功。市面上的打字练习工具要么功能臃肿、要么需要注册登录、要么充满广告。我想要的是一个打开即用、零依赖、足够好看的小工具——于是有了这个项目。
目标很明确:
- 单 HTML 文件,不依赖任何框架和 CDN
- 支持中英文混合练习
- 实时反馈打字正确率与速度
- 暗色主题,护眼且现代
二、技术路线选型
框架:无
这是最关键的决策。一个打字游戏的全部逻辑不超过 300 行 JS,引入 React/Vue 的收益远小于它带来的体积和复杂度。纯原生 JS + DOM 操作完全够用,而且:
- 零构建步骤,改完刷新即生效
- 零
node_modules,仓库克隆即可运行 - 离线可用,不依赖任何 CDN
样式:内联 CSS + CSS 变量
所有 CSS 内联在 <style> 标签中,使用 CSS 自定义属性(CSS Variables)构建主题系统:
:root {
--bg: #11111b;
--correct: #a6e3a1;
--incorrect: #f38ba8;
--cursor: #f5c2e7;
}
好处是换肤只需改 :root 的值,不用预处理器。配色选了 [Catppuccin]Mocha 主题——社区流行的暗色配色方案,色板经过精心调校,对比度舒适。
字体:系统字体栈
font-family: "Segoe UI", "PingFang SC", "Microsoft YaHei", "Noto Sans CJK SC", system-ui, sans-serif;
不加载任何外部字体,依赖系统预装字体。跨平台一致性靠字体栈 fallback 保证:Windows 用 Segoe UI + 微软雅黑,macOS 用 PingFang SC,Linux 用 Noto Sans CJK SC。
三、核心技术实现
1. 文本高亮渲染
核心思路是把目标文本拆成单个字符,每个字符用 <span> 包裹,根据当前输入进度赋予不同 class:
text.split('').map((char, i) => {
if (i < currentIndex) {
return `<span class="correct">${char}</span>`; // 已正确输入
} else if (i === currentIndex) {
return `<span class="cursor">${char}</span>`; // 当前位置
} else {
return `<span class="pending">${char}</span>`; // 未输入
}
}).join('');
每次输入后重新渲染整个文本区域。对于几百字的文本,这个性能开销可以忽略。如果文本量更大,可以改为只更新变化的 span,但当前场景没必要过早优化。
2. 中文输入法(IME)处理
这是最容易被忽略的坑。中文输入时,输入法会经历候选→确认的过程,期间会触发 input 事件但内容还不是最终字符。如果不处理,候选阶段的每个拼音字母都会被误判为错误输入。
解决方案是监听 compositionstart 和 compositionend 事件:
let isComposing = false;
input.addEventListener('compositionstart', () => {
isComposing = true;
});
input.addEventListener('compositionend', (e) => {
isComposing = false;
// 在这里处理最终输入的中文字符
handleInput(e.data);
});
input.addEventListener('input', (e) => {
if (isComposing) return; // 候选阶段跳过
handleInput(e.data);
});
3. WPM 计算
WPM(Words Per Minute)的标准定义是每分钟输入的"标准词"数,一个标准词 = 5 个字符:
function calculateWPM() {
const minutes = elapsedTime / 60;
const words = correctChars / 5;
return Math.round(words / minutes);
}
计时从第一个字符输入开始,到文本全部正确输入结束。
4. 难度分级
三档难度对应不同的文本池:
| 难度 | 文本来源 | 字符量 |
|---|---|---|
| 简单 | 常见英文单词随机组合 | ~30-50 |
| 中等 | 英文名言/句子 | ~80-120 |
| 困难 | 中文段落(技术/文学) | ~100-200 |
每次开始游戏从对应池中随机选取一条文本。
四、实战心得
心得1:单文件不是限制,是优势
开发过程中,所有代码在一个文件里,跳转定义就是上下滚动,不需要 IDE 的 Go to Definition。调试时打开浏览器 DevTools,HTML/CSS/JS 全在一处,改完刷新即生效。
这种开发体验反而比 webpack 热更新更快——因为没有构建步骤。
心得2:CSS 变量比预处理器更轻
以前习惯用 SCSS 变量,但 CSS 自定义属性是浏览器原生支持的,不需要编译。而且它可以在运行时通过 JS 修改:
document.documentElement.style.setProperty('--cursor', '#ff0000');
这意味着动态换肤不需要切换 stylesheet,改几个变量就行。
心得3:IME 处理是中文 Web 应用的必修课
任何涉及文本输入的中文 Web 应用都必须正确处理 IME 事件。这不是边缘情况——在中国,几乎所有用户都用输入法输入中文。忽略 compositionstart/compositionend 会导致输入体验严重劣化。
心得4:暗色主题的对比度要精心调
不是所有暗色都护眼。纯黑背景 + 纯白文字的对比度太高,长时间看反而刺眼。Catppuccin Mocha 的背景是 #11111b(不是纯黑),文字是 #cdd6f4(不是纯白),这种"低对比度暗色"更舒适。
五、存在问题与优化空间
问题1:文本库太小
当前每个难度的文本池只有十几条,练习几次就会重复。优化方向:
- 扩大文本库,每档至少 50+ 条
- 支持用户自定义文本(粘贴一段文字来练习)
- 从 API 拉取每日热文作为练习材料
问题2:缺少历史记录
没有保存用户的练习历史,无法看到进步曲线。优化方向:
- 用
localStorage保存最近 20 次练习的 WPM/准确率 - 在完成界面展示历史趋势图(Canvas 绘制折线图)
问题3:移动端体验未优化
键盘事件在移动端表现不一致,且虚拟键盘会遮挡界面。优化方向:
- 检测移动端,使用
contenteditable替代<input> - 调整布局,输入区域上移避开虚拟键盘
问题4:无音效反馈
打字游戏的爽感很大程度来自音效。当前版本完全静音。优化方向:
- 用 Web Audio API 生成按键音(不需要音频文件)
- 正确输入和错误输入用不同音调
- 完成时播放胜利音效
问题5:无多人对战模式
打字游戏的社交性是留存关键。优化方向:
- 用 WebSocket 实现实时对战
- 显示对手的输入进度条
- 排行榜系统
六、技术路线总结
需求分析 → 确定单文件方案 → 设计 CSS 变量主题系统
→ 实现文本高亮渲染 → 处理 IME 中文输入
→ 实现统计计算 → 添加难度分级
→ 设计完成界面 → 调优动画与视觉
整体开发约 2 小时,代码量 ~500 行(HTML + CSS + JS)。这个项目证明了:不是所有项目都需要框架,选择合适的工具比选择流行的工具更重要。
七、给同样想做小工具的开发者的建议
- 先问"能不能不用框架"——如果答案是能,那就不用
- CSS 变量 + 系统字体栈 是轻量主题化的最佳方案
- IME 事件处理 是中文输入场景的必选项,不是可选项
- 暗色主题 选低对比度配色,别用纯黑纯白
- 单文件部署 是小工具的最优分发方式——一个链接就能用
本文为原创技术分享,转载请注明出处。
- 点赞
- 收藏
- 关注作者
评论(0)