# 原生JS面向對象如何實現打字小游戲
## 一、前言
在Web開發中,JavaScript作為核心語言,其面向對象編程(OOP)能力常被忽視。本文將使用原生JS面向對象技術,從零開始構建一個完整的打字小游戲。通過這個項目,您將掌握:
1. ES6類(class)的運用
2. 游戲狀態管理
3. DOM操作優化
4. 動畫與定時器控制
5. 鍵盤事件處理
## 二、項目結構與設計
### 2.1 游戲核心模塊
```javascript
class TypingGame {
constructor(config) {
// 初始化游戲配置
this.difficulty = config.difficulty || 'normal';
this.timeLimit = config.timeLimit || 60;
this.currentScore = 0;
this.isPlaying = false;
this.timer = null;
this.wordsPool = [];
}
// 其他方法...
}
┌───────────────────┐ ┌───────────────────┐
│ TypingGame │ │ WordItem │
├───────────────────┤ ├───────────────────┤
│ - difficulty │ │ - word │
│ - timeLimit │ │ - speed │
│ - currentScore │ │ - position │
│ - isPlaying │ │ - element │
│ - timer │ ├───────────────────┤
│ - wordsPool │ │ + move() │
├───────────────────┤ │ + createElement() │
│ + startGame() │ └───────────────────┘
│ + endGame() │
│ + updateScore() │
│ + generateWord() │
└───────────────────┘
class WordItem {
constructor(word, options) {
this.word = word;
this.speed = options.speed || 2;
this.position = { x: 0, y: 0 };
this.element = null;
this.init();
}
init() {
this.createElement();
this.setInitialPosition();
}
createElement() {
const wordEl = document.createElement('div');
wordEl.className = 'word-item';
wordEl.textContent = this.word;
document.getElementById('game-area').appendChild(wordEl);
this.element = wordEl;
}
setInitialPosition() {
const gameArea = document.getElementById('game-area');
this.position = {
x: Math.random() * (gameArea.offsetWidth - 200),
y: 0
};
this.updatePosition();
}
move() {
this.position.y += this.speed;
this.updatePosition();
return this.position.y > document.getElementById('game-area').offsetHeight;
}
updatePosition() {
this.element.style.transform = `translate(${this.position.x}px, ${this.position.y}px)`;
}
}
class TypingGame {
// ...constructor...
startGame() {
if (this.isPlaying) return;
this.isPlaying = true;
this.currentScore = 0;
this.updateScoreDisplay();
// 初始化單詞池
this.initWordsPool();
// 啟動游戲循環
this.gameLoop();
// 設置倒計時
this.startTimer();
// 綁定鍵盤事件
this.bindEvents();
}
gameLoop() {
if (!this.isPlaying) return;
// 隨機生成新單詞
if (Math.random() < 0.03) {
this.generateWord();
}
// 移動所有單詞
this.wordsPool.forEach((word, index) => {
if (word.move()) {
// 單詞超出底部邊界
word.element.remove();
this.wordsPool.splice(index, 1);
this.updateScore(-10); // 扣分
}
});
requestAnimationFrame(() => this.gameLoop());
}
generateWord() {
const words = ['JavaScript', 'TypeScript', 'React', 'Vue', 'Angular',
'Node.js', 'Webpack', 'ES6', 'Promise', 'Async'];
const word = words[Math.floor(Math.random() * words.length)];
const speed = this.difficulty === 'hard' ? 3 :
this.difficulty === 'normal' ? 2 : 1;
const newWord = new WordItem(word, { speed });
this.wordsPool.push(newWord);
}
handleInput(inputWord) {
let found = false;
this.wordsPool.forEach((word, index) => {
if (word.word === inputWord) {
word.element.remove();
this.wordsPool.splice(index, 1);
this.updateScore(inputWord.length * 2); // 按長度計分
found = true;
}
});
return found;
}
}
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<title>JS打字游戲</title>
<style>
#game-container {
width: 800px;
height: 500px;
margin: 0 auto;
position: relative;
border: 2px solid #333;
overflow: hidden;
}
#game-area {
width: 100%;
height: 100%;
background-color: #f0f0f0;
}
#game-ui {
position: absolute;
top: 10px;
left: 10px;
z-index: 100;
}
.word-item {
position: absolute;
font-size: 24px;
color: #333;
white-space: nowrap;
transition: transform 0.1s linear;
}
#input-area {
margin-top: 20px;
text-align: center;
}
#word-input {
padding: 8px;
font-size: 16px;
}
</style>
</head>
<body>
<div id="game-container">
<div id="game-ui">
<div>分數: <span id="score">0</span></div>
<div>時間: <span id="time">60</span>秒</div>
</div>
<div id="game-area"></div>
</div>
<div id="input-area">
<input type="text" id="word-input" placeholder="輸入看到的單詞...">
</div>
<script src="typing-game.js"></script>
</body>
</html>
// typing-game.js
class WordItem {
constructor(word, options = {}) {
this.word = word;
this.speed = options.speed || 2;
this.position = { x: 0, y: 0 };
this.element = null;
this.init();
}
init() {
this.createElement();
this.setInitialPosition();
}
createElement() {
const wordEl = document.createElement('div');
wordEl.className = 'word-item';
wordEl.textContent = this.word;
document.getElementById('game-area').appendChild(wordEl);
this.element = wordEl;
}
setInitialPosition() {
const gameArea = document.getElementById('game-area');
this.position = {
x: Math.random() * (gameArea.offsetWidth - 200),
y: 0
};
this.updatePosition();
}
move() {
this.position.y += this.speed;
this.updatePosition();
return this.position.y > document.getElementById('game-area').offsetHeight;
}
updatePosition() {
this.element.style.transform = `translate(${this.position.x}px, ${this.position.y}px)`;
}
}
class TypingGame {
constructor(config) {
this.config = {
difficulty: 'normal',
timeLimit: 60,
...config
};
this.currentScore = 0;
this.isPlaying = false;
this.timer = null;
this.wordsPool = [];
this.inputWord = '';
this.init();
}
init() {
this.bindEvents();
}
startGame() {
if (this.isPlaying) return;
this.isPlaying = true;
this.currentScore = 0;
this.updateScoreDisplay();
// 清空現有單詞
this.clearWords();
// 啟動游戲循環
this.gameLoop();
// 設置倒計時
this.startTimer();
}
endGame() {
this.isPlaying = false;
clearInterval(this.timer);
this.timer = null;
this.clearWords();
alert(`游戲結束! 最終得分: ${this.currentScore}`);
}
clearWords() {
this.wordsPool.forEach(word => word.element.remove());
this.wordsPool = [];
}
gameLoop() {
if (!this.isPlaying) return;
// 隨機生成新單詞
if (Math.random() < this.getSpawnRate()) {
this.generateWord();
}
// 移動所有單詞
this.wordsPool.forEach((word, index) => {
if (word.move()) {
// 單詞超出底部邊界
word.element.remove();
this.wordsPool.splice(index, 1);
this.updateScore(-10); // 扣分
}
});
requestAnimationFrame(() => this.gameLoop());
}
getSpawnRate() {
switch (this.config.difficulty) {
case 'easy': return 0.02;
case 'normal': return 0.03;
case 'hard': return 0.05;
default: return 0.03;
}
}
generateWord() {
const words = this.getWordList();
const word = words[Math.floor(Math.random() * words.length)];
const speed = this.getWordSpeed();
const newWord = new WordItem(word, { speed });
this.wordsPool.push(newWord);
}
getWordList() {
return [
'function', 'variable', 'constant', 'object', 'array',
'string', 'number', 'boolean', 'null', 'undefined',
'class', 'constructor', 'method', 'property', 'inheritance',
'encapsulation', 'polymorphism', 'abstraction', 'interface',
'prototype', 'closure', 'callback', 'promise', 'async',
'await', 'module', 'import', 'export', 'spread',
'destructuring', 'arrow', 'template', 'literal', 'generator',
'iterator', 'symbol', 'proxy', 'reflect', 'set', 'map'
];
}
getWordSpeed() {
switch (this.config.difficulty) {
case 'easy': return 1;
case 'normal': return 2;
case 'hard': return 3;
default: return 2;
}
}
startTimer() {
let timeLeft = this.config.timeLimit;
document.getElementById('time').textContent = timeLeft;
this.timer = setInterval(() => {
timeLeft--;
document.getElementById('time').textContent = timeLeft;
if (timeLeft <= 0) {
this.endGame();
}
}, 1000);
}
updateScore(points) {
this.currentScore += points;
if (this.currentScore < 0) this.currentScore = 0;
this.updateScoreDisplay();
}
updateScoreDisplay() {
document.getElementById('score').textContent = this.currentScore;
}
bindEvents() {
const inputEl = document.getElementById('word-input');
inputEl.addEventListener('keydown', (e) => {
if (e.key === 'Enter') {
this.handleInput(inputEl.value.trim());
inputEl.value = '';
}
});
// 開始游戲按鈕
document.getElementById('start-btn')?.addEventListener('click', () => {
this.startGame();
inputEl.focus();
});
}
handleInput(inputWord) {
if (!this.isPlaying || !inputWord) return;
let found = false;
// 從后往前查找,避免splice影響索引
for (let i = this.wordsPool.length - 1; i >= 0; i--) {
const word = this.wordsPool[i];
if (word.word === inputWord) {
word.element.remove();
this.wordsPool.splice(i, 1);
this.updateScore(inputWord.length * 2); // 按長度計分
found = true;
break; // 只匹配第一個找到的單詞
}
}
if (!found) {
this.updateScore(-5); // 輸入錯誤扣分
}
}
}
// 初始化游戲
const game = new TypingGame({
difficulty: 'normal',
timeLimit: 60
});
// 暴露全局變量便于測試
window.game = game;
通過這個打字游戲項目,我們實踐了以下面向對象編程原則:
原生JS面向對象開發不僅能構建復雜應用,還能幫助開發者深入理解JavaScript語言特性。這種開發模式在大型項目中尤其重要,能顯著提高代碼的可維護性和可擴展性。 “`
免責聲明:本站發布的內容(圖片、視頻和文字)以原創、轉載和分享為主,文章觀點不代表本網站立場,如果涉及侵權請聯系站長郵箱:is@yisu.com進行舉報,并提供相關證據,一經查實,將立刻刪除涉嫌侵權內容。