📖 項目簡介
英語單詞消消樂?是一款專為英語學習設計的互動式記憶游戲。通過經典的消消樂玩法,讓用戶在輕松愉快的游戲中掌握英語單詞,提高詞匯量和記憶效果。
🎯 項目目標
- 讓英語學習變得有趣且高效
- 通過游戲化方式增強單詞記憶
- 提供多樣化的單詞庫選擇
- 支持語音朗讀功能,提升聽說能力
? 核心功能特色
🎮 游戲玩法
- 經典消消樂模式:點擊英語單詞和對應的中文翻譯來消除
- 智能匹配系統:自動識別英文-中文配對
- 實時反饋:匹配成功有消失動畫,失敗有抖動提示
- 進度追蹤:實時顯示匹配進度和得分
🗣? 語音功能
- 雙語朗讀:支持英語和中文語音朗讀
- 智能語音:根據卡片類型自動選擇語言
- 可調節語速:英語稍慢便于學習,中文正常語速
- 語音開關:用戶可自由開啟/關閉語音功能
📚 單詞庫系統
- 多樣化主題:水果、動物、顏色、數字等多個主題
- 易于擴展:模塊化設計,可輕松添加新單詞庫
- 實時切換:游戲過程中可隨時切換單詞庫
🎨 用戶體驗
- 響應式設計:完美適配手機、平板、電腦
- 精美動畫:流暢的卡片動畫和交互效果
- 直觀界面:簡潔美觀的 UI 設計
- 即時反饋:清晰的操作提示和狀態顯示
🛠? 技術棧
- React.js?– 現代化的用戶界面框架
- Tailwind CSS?– 實用優先的 CSS 框架
- JavaScript ES6+?– 現代 JavaScript 語法
- Web Speech API?– 瀏覽器原生語音合成
全部源碼
import { useState, useEffect } from "react";
import { wordLibraries } from "../data/wordLibraries";export default function WordsGame() {const [currentLibrary, setCurrentLibrary] = useState("fruits");const [gameWords, setGameWords] = useState([]);const [selectedCards, setSelectedCards] = useState([]);const [matchedPairs, setMatchedPairs] = useState([]);const [score, setScore] = useState(0);const [gameComplete, setGameComplete] = useState(false);const [gameStarted, setGameStarted] = useState(false);const [disappearingCards, setDisappearingCards] = useState([]);const [shakingCards, setShakingCards] = useState([]);const [speechSynthesis, setSpeechSynthesis] = useState(null);const [speechEnabled, setSpeechEnabled] = useState(true);// 初始化游戲const initializeGame = () => {const library = wordLibraries[currentLibrary];const allWords = [...library.words];// 隨機選擇10對單詞(20張卡片)const shuffled = allWords.sort(() => Math.random() - 0.5).slice(0, 10);// 創建卡片數組,每對單詞創建兩張卡片const cards = [];shuffled.forEach((word, index) => {cards.push({id: `english-${index}`,type: "english",content: word.english,pairId: index,matched: false,});cards.push({id: `chinese-${index}`,type: "chinese",content: word.chinese,pairId: index,matched: false,});});// 隨機打亂卡片順序const shuffledCards = cards.sort(() => Math.random() - 0.5);setGameWords(shuffledCards);setSelectedCards([]);setMatchedPairs([]);setScore(0);setGameComplete(false);setGameStarted(true);};// 處理卡片點擊const handleCardClick = (card) => {if (card.matched || selectedCards.length >= 2) return;// 朗讀卡片內容speakCardContent(card);const newSelectedCards = [...selectedCards, card];setSelectedCards(newSelectedCards);if (newSelectedCards.length === 2) {const [card1, card2] = newSelectedCards;// 檢查是否匹配if (card1.pairId === card2.pairId && card1.type !== card2.type) {// 匹配成功 - 添加溫和的消失動畫setDisappearingCards([card1.id, card2.id]);setTimeout(() => {setMatchedPairs((prev) => [...prev, card1.pairId]);setScore((prev) => prev + 10);setSelectedCards([]);setDisappearingCards([]);// 檢查游戲是否完成if (matchedPairs.length + 1 === 10) {setGameComplete(true);}}, 400);} else {// 匹配失敗 - 添加抖動動畫和標紅效果setShakingCards([card1.id, card2.id]);setTimeout(() => {setSelectedCards([]);setShakingCards([]); // 動畫結束后復位}, 1000);}}};// 切換單詞庫const changeLibrary = (libraryKey) => {setCurrentLibrary(libraryKey);setGameStarted(false);};// 檢查卡片是否被選中const isCardSelected = (card) => {return selectedCards.some((selected) => selected.id === card.id);};// 檢查卡片是否已匹配const isCardMatched = (card) => {return matchedPairs.includes(card.pairId);};// 檢查卡片是否正在消失const isCardDisappearing = (card) => {return disappearingCards.includes(card.id);};// 檢查卡片是否正在抖動const isCardShaking = (card) => {return shakingCards.includes(card.id);};// 朗讀卡片內容const speakCardContent = (card) => {if (speechEnabled && speechSynthesis && !speechSynthesis.speaking) {const utterance = new SpeechSynthesisUtterance(card.content);// 根據卡片類型設置語言if (card.type === "english") {utterance.lang = "en-US";utterance.rate = 0.8; // 稍微慢一點,便于學習} else {utterance.lang = "zh-CN";utterance.rate = 0.9;}utterance.volume = 0.8;utterance.pitch = 1.0;speechSynthesis.speak(utterance);}};// 測試語音功能const testSpeech = () => {if (speechSynthesis) {const testText = "語音功能測試";const utterance = new SpeechSynthesisUtterance(testText);utterance.lang = "zh-CN";utterance.rate = 0.9;utterance.volume = 0.8;speechSynthesis.speak(utterance);}};useEffect(() => {if (gameStarted) {initializeGame();}}, [currentLibrary]);// 初始化語音合成useEffect(() => {if ("speechSynthesis" in window) {setSpeechSynthesis(window.speechSynthesis);}}, []);return (<div className="min-h-screen bg-gradient-to-br from-blue-50 via-indigo-50 to-purple-50 py-4 px-4"><div className="max-w-6xl mx-auto">{/* 標題和單詞庫選擇 */}<div className="flex flex-col lg:flex-row justify-between items-center mb-4 gap-4"><div className="text-center flex-1"><h1 className="text-xl md:text-2xl lg:text-3xl font-bold text-indigo-700 mb-1 animate-bounce">🎮 英語單詞消消樂</h1><p className="text-xs md:text-sm lg:text-base text-indigo-600 font-medium">點擊英語單詞和對應的中文翻譯來消除吧!</p></div>{/* 單詞庫選擇和語音開關 */}<div className="flex flex-col sm:flex-row items-center gap-3">{/* 語音開關 */}<div className="bg-gradient-to-r from-emerald-100 to-teal-100 rounded-xl shadow-md p-2 md:p-3 border-2 border-emerald-200"><div className="flex items-center space-x-1 md:space-x-2"><span className="text-xs md:text-sm font-bold text-emerald-700">{speechEnabled ? "🔊" : "🔇"}</span><buttononClick={() => setSpeechEnabled(!speechEnabled)}className={`px-2 md:px-3 py-1 text-xs md:text-sm font-bold rounded-lg border-2 transition-all duration-200 cursor-pointer ${speechEnabled? "bg-emerald-500 text-white border-emerald-400": "bg-gray-300 text-gray-600 border-gray-400"}`}>{speechEnabled ? "語音開" : "語音關"}</button><buttononClick={testSpeech}className="px-1 md:px-2 py-1 text-xs font-bold bg-blue-500 text-white rounded border-2 border-blue-400 transition-all duration-200 cursor-pointer hover:bg-blue-600">測試</button></div></div>{/* 單詞庫選擇 */}<div className="bg-gradient-to-r from-indigo-100 to-purple-100 rounded-xl shadow-md p-2 md:p-3 border-2 border-indigo-200"><div className="flex items-center"><span className="text-xs md:text-sm font-bold text-indigo-700 mr-1 md:mr-2">🌈</span><selectvalue={currentLibrary}onChange={(e) => changeLibrary(e.target.value)}className="px-2 md:px-3 py-1 text-xs md:text-sm font-bold text-indigo-700 bg-white rounded-lg border-2 border-indigo-200 shadow-sm focus:outline-none focus:border-purple-400 transition-all duration-200 cursor-pointer">{Object.entries(wordLibraries).map(([key, library]) => (<optionkey={key}value={key}className="text-xs md:text-sm font-bold">{library.name}</option>))}</select></div></div></div></div>{/* 游戲狀態 */}<div className="bg-gradient-to-r from-emerald-100 to-teal-100 rounded-xl shadow-md p-3 mb-4 border-2 border-emerald-200"><div className="flex justify-between items-center mb-3"><div className="text-sm md:text-base font-bold text-emerald-700">🎯 {wordLibraries[currentLibrary].name}</div><div className="text-lg md:text-xl font-bold text-indigo-600">? {score}</div></div>{!gameStarted ? (<buttononClick={initializeGame}className="w-full bg-gradient-to-r from-emerald-500 to-teal-600 hover:from-emerald-600 hover:to-teal-700 text-white font-bold py-2 px-4 rounded-xl text-base md:text-lg shadow-md transform hover:scale-105 transition-all duration-200 border-2 border-emerald-400">🚀 開始游戲</button>) : (<div className="text-center"><div className="text-sm md:text-base text-emerald-600 mb-2 font-bold">🎪 {matchedPairs.length} / 10</div><buttononClick={initializeGame}className="bg-gradient-to-r from-indigo-500 to-purple-600 hover:from-indigo-600 hover:to-purple-700 text-white font-bold py-1 px-3 rounded-lg text-sm md:text-base shadow-md transform hover:scale-105 transition-all duration-200 border-2 border-indigo-400">🔄 重新開始</button></div>)}</div>{/* 游戲完成提示 */}{gameComplete && (<div className="bg-gradient-to-r from-emerald-200 to-indigo-200 border-2 border-emerald-400 text-emerald-800 px-4 py-3 rounded-xl mb-4 text-center shadow-md animate-pulse"><h3 className="text-xl md:text-2xl font-bold mb-2">🎉 恭喜!游戲完成!🎉</h3><p className="text-lg font-bold">最終得分: ? {score} ?</p></div>)}{/* 游戲卡片區域 */}{gameStarted && (<div className="relative"><div className="grid grid-cols-4 sm:grid-cols-5 md:grid-cols-8 lg:grid-cols-10 gap-1 md:gap-2">{gameWords.map((card) => (<divkey={card.id}onClick={() => !isCardMatched(card) && handleCardClick(card)}className={`h-16 sm:h-20 md:h-24 rounded-lg md:rounded-xl shadow-md transition-all duration-300 transform${isCardMatched(card)? "opacity-0 pointer-events-none": "cursor-pointer hover:scale-105 hover:rotate-1"}${isCardDisappearing(card)? "animate-disappear pointer-events-none bg-gradient-to-br from-indigo-300 to-purple-300 border-2 border-indigo-500 shadow-lg": isCardShaking(card)? "animate-shake pointer-events-none bg-gradient-to-br from-red-200 to-pink-200 border-2 border-red-400 shadow-md": isCardSelected(card)? "bg-gradient-to-br from-indigo-200 to-purple-200 border-2 border-indigo-400 shadow-md": "bg-gradient-to-br from-slate-50 to-gray-100 border-2 border-slate-200 hover:border-indigo-400 hover:shadow-md"}`}><div className="flex items-center justify-center h-full p-1 md:p-2 w-full"><spanclassName={`text-xs md:text-sm font-bold text-center word-card-text w-full${card.type === "english"? "text-indigo-700": "text-emerald-700"}`}>{card.content}</span></div></div>))}</div></div>)}{/* 游戲說明 */}<div className="bg-gradient-to-r from-slate-100 to-gray-100 rounded-xl shadow-md p-3 mt-4 border-2 border-slate-200"><h3 className="text-lg font-bold text-slate-700 mb-3 text-center">📖 游戲規則</h3><ul className="text-slate-700 space-y-2 text-sm font-medium"><li className="flex items-center">🎯 點擊英語單詞和對應的中文翻譯來消除</li><li className="flex items-center">👆 每次只能選擇兩張卡片</li><li className="flex items-center">? 匹配成功得10分,匹配失敗不扣分</li><li className="flex items-center">🏆 消除所有卡片即可完成游戲</li><li className="flex items-center">🔄 可以隨時切換不同的單詞庫</li><li className="flex items-center">🔊 點擊卡片時會朗讀內容,可開關語音功能</li></ul></div></div></div>);
}
React 英語單詞消消樂,一款專為英語學習設計的互動式記憶游戲 - 高質量源碼分享平臺-免費下載各類網站源碼與模板及前沿動態資訊