所有分类
  • 所有分类
  • Html5资源
  • React资源
  • Vue资源
  • Php资源
  • ‌小程序资源
  • Python资源

React 英语单词消消乐一款专为英语学习设计的互动式记忆游戏

获取 Demo 源代码请在公众号回复“react单词消消乐” 。

📖 项目简介

英语单词消消乐 是一款专为英语学习设计的互动式记忆游戏。通过经典的消消乐玩法,让用户在轻松愉快的游戏中掌握英语单词,提高词汇量和记忆效果。

🎯 项目目标

  • 让英语学习变得有趣且高效
  • 通过游戏化方式增强单词记忆
  • 提供多样化的单词库选择
  • 支持语音朗读功能,提升听说能力

✨ 核心功能特色

🎮 游戏玩法

  • 经典消消乐模式:点击英语单词和对应的中文翻译来消除
  • 智能匹配系统:自动识别英文-中文配对
  • 实时反馈:匹配成功有消失动画,失败有抖动提示
  • 进度追踪:实时显示匹配进度和得分

🗣️ 语音功能

  • 双语朗读:支持英语和中文语音朗读
  • 智能语音:根据卡片类型自动选择语言
  • 可调节语速:英语稍慢便于学习,中文正常语速
  • 语音开关:用户可自由开启/关闭语音功能

📚 单词库系统

  • 多样化主题:水果、动物、颜色、数字等多个主题
  • 易于扩展:模块化设计,可轻松添加新单词库
  • 实时切换:游戏过程中可随时切换单词库

🎨 用户体验

  • 响应式设计:完美适配手机、平板、电脑
  • 精美动画:流畅的卡片动画和交互效果
  • 直观界面:简洁美观的 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>
                <button
                  onClick={() => 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>
                <button
                  onClick={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>
                <select
                  value={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]) => (
                    <option
                      key={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 ? (
            <button
              onClick={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>
              <button
                onClick={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) => (
                <div
                  key={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">
                    <span
                      className={`
                        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>
  );
}
资源下载
下载价格免费
注意:本网站资源属于虚拟产品,不支持退款。请谨慎购买! 购买后资源无法下载,请联系客服QQ:844475003,微信号:th844475003。
原文链接:https://code.ifrontend.net/archives/729,转载请注明出处。
0

评论0

显示验证码
没有账号?注册  忘记密码?