React 快乐记单词——儿童英语拼写游戏

发布于:2025-07-11 ⋅ 阅读:(44) ⋅ 点赞:(0)

📖 项目简介

快乐记单词 是一款专为儿童设计的互动式英语拼写学习游戏。通过有趣的游戏化体验,让孩子们在轻松愉快的氛围中掌握英语单词的拼写,提高英语学习兴趣和效果。

✨ 核心特色

🎮 游戏化学习体验

  • 互动拼写:通过点击字母按钮完成单词拼写
  • 即时反馈:实时验证拼写正确性,给予鼓励性反馈
  • 进度追踪:记录学习进度和正确率统计
  • 成就系统:根据表现给予不同的鼓励语

🎨 精美视觉设计

  • 可爱界面:采用粉色和蓝色为主的温馨配色
  • 动画效果:字母按钮悬停动画、倒计时动画等
  • 装饰元素:底部可爱的水果和动物表情装饰
  • 响应式设计:完美适配手机、平板和电脑

🔊 多媒体功能

  • 语音播放:点击中文释义可播放英文发音
  • 发音标准:使用浏览器原生语音合成技术
  • 交互反馈:音效和视觉反馈相结合

🎯 目标用户

  • 学龄前儿童:3-6 岁英语启蒙阶段
  • 小学低年级学生:1-3 年级英语学习
  • 英语初学者:任何年龄的英语入门学习者
  • 家长和老师:用于教学和辅导

📚 丰富的单词库

🍎 生活主题词汇

  • 水果类:apple, banana, orange, grape, strawberry 等
  • 动物类:cat, dog, elephant, tiger, lion 等
  • 颜色类:red, blue, green, yellow, purple 等
  • 数字类:one, two, three, four, five 等

👨‍👩‍👧‍👦 家庭和社交

  • 家庭成员:father, mother, brother, sister 等
  • 食物类:bread, rice, noodles, egg, milk 等
  • 学校用品:book, pen, pencil, eraser, ruler 等

📖 系统化学习

  • Unit 1-4:按教学单元组织的词汇
  • 循序渐进:从简单到复杂的词汇安排
  • 实用性强:贴近日常生活的常用词汇

🚀 技术特色

  • React 18:最新的 React 框架
  • Vite:快速的构建工具
  • Tailwind CSS:现代化的样式框架
  • 响应式设计:完美适配各种设备
  • 流畅动画:60fps 的流畅交互体验
  • 智能反馈:根据学习表现提供个性化鼓励
  • 无障碍设计:支持键盘操作和屏幕阅读器
  • 离线可用:纯前端应用,无需网络连接

🎮 游戏玩法

  • 选择单词库:右上角下拉菜单选择学习主题
  • 查看中文释义:点击中文释义可播放英文发音
  • 拼写单词:点击字母按钮按顺序拼写单词
  • 验证答案:点击”验证”按钮检查拼写是否正确
  • 继续学习:点击”下一个”进入下一个单词
  • 正确反馈:拼写正确时显示绿色对勾和鼓励语
  • 错误提示:拼写错误时显示红色叉号,可重新尝试
  • 进度统计:实时显示正确率和完成进度
  • 成就奖励:完成一轮后根据表现给予不同等级的鼓励

📱 使用场景

  • 亲子互动:家长陪伴孩子一起学习
  • 自主学习:孩子独立使用,培养学习习惯
  • 复习巩固:课后复习和巩固课堂所学
  • 课堂辅助:老师用于课堂教学演示
  • 课后作业:布置给学生的家庭作业
  • 兴趣小组:英语兴趣小组的活动工具

📊 学习效果

  • 词汇量提升:系统掌握常用英语词汇
  • 拼写能力:提高英语单词拼写准确率
  • 学习兴趣:培养英语学习兴趣和自信心
  • 自主学习:培养独立学习能力

 

源码

import React, { useState } from "react";
import { wordLibraries } from "../data/wordLibraries";

// 洗牌算法
function shuffle(arr) {
  // 洗牌算法
  const a = [...arr];
  for (let i = a.length - 1; i > 0; i--) {
    const j = Math.floor(Math.random() * (i + 1));
    [a[i], a[j]] = [a[j], a[i]];
  }
  return a;
}

export default function CutWatermelonGame() {
  const [bankKey, setBankKey] = useState(Object.keys(wordLibraries)[0]);
  const [currentIdx, setCurrentIdx] = useState(0);
  const [order, setOrder] = useState(
    shuffle(
      Array.from({ length: wordLibraries[bankKey].words.length }, (_, i) => i)
    )
  );
  const [input, setInput] = useState([]);
  const [feedback, setFeedback] = useState("");
  const [shuffled, setShuffled] = useState([]);
  const [countdown, setCountdown] = useState(0);
  const [showCountdown, setShowCountdown] = useState(false);
  const [showFinish, setShowFinish] = useState(false);
  const [correctCount, setCorrectCount] = useState(0);
  const [totalCount, setTotalCount] = useState(0);
  const [hasAnswered, setHasAnswered] = useState(false);

  const words = wordLibraries[bankKey].words;
  const currentWord = words[order[currentIdx]];

  // 初始化打乱字母和顺序(切换库时重洗顺序)
  React.useEffect(() => {
    setInput([]);
    setFeedback("");
    setShuffled(shuffle(currentWord.english.split("")));
  }, [bankKey, currentIdx, order]);

  // 切换库时重置顺序
  React.useEffect(() => {
    setOrder(shuffle(Array.from({ length: words.length }, (_, i) => i)));
    setCurrentIdx(0);
    setHasAnswered(false);
  }, [bankKey]);

  // 点击字母拼接
  const handleLetterClick = (letter, idx) => {
    setInput([...input, { letter, idx }]);
  };

  // 提交答案
  const handleSubmit = () => {
    if (hasAnswered) return;
    const answer = input.map((i) => i.letter).join("");
    if (answer === currentWord.english) {
      setFeedback("✅ 恭喜你,拼写正确!");
      setCountdown(3);
      setShowCountdown(true);
      setCorrectCount((c) => c + 1);
    } else {
      setFeedback("❌ 拼写不对,再试试!");
    }
    setTotalCount((t) => t + 1);
    setHasAnswered(true);
  };

  // 下一题
  const handleNext = () => {
    if (!hasAnswered) {
      setTotalCount((t) => t + 1);
      setHasAnswered(true);
    }
    if (currentIdx + 1 < words.length) {
      setCurrentIdx((prev) => prev + 1);
    } else {
      // 全部做完,弹窗鼓励
      setShowFinish(true);
    }
    setInput([]);
    setFeedback("");
    setHasAnswered(false);
  };

  // 重置本库
  const handleReset = () => {
    setOrder(shuffle(Array.from({ length: words.length }, (_, i) => i)));
    setCurrentIdx(0);
    setInput([]);
    setFeedback("");
    setShowFinish(false);
    setCorrectCount(0);
    setTotalCount(0);
  };

  // 切换单词库
  const handleBankChange = (e) => {
    setBankKey(e.target.value);
    setCurrentIdx(0);
    setInput([]);
    setFeedback("");
  };

  // 判断字母是否已被用
  const usedIdxs = input.map((i) => i.idx);

  // 倒计时副作用
  React.useEffect(() => {
    if (showCountdown && countdown > 0) {
      const timer = setTimeout(() => {
        setCountdown(countdown - 1);
      }, 1000);
      return () => clearTimeout(timer);
    } else if (showCountdown && countdown === 0) {
      setShowCountdown(false);
      handleNext();
    }
    // eslint-disable-next-line
  }, [showCountdown, countdown]);

  // 鼓励语函数
  function getFinishTitle() {
    if (totalCount === 0) return "太棒了!本轮单词全部完成!";
    const rate = correctCount / totalCount;
    if (rate === 1) return "完美!全部答对!";
    if (rate >= 0.8) return "很棒!继续努力!";
    if (rate >= 0.5) return "不错哦!再接再厉!";
    return "加油!下次会更好!";
  }

  function getFinishDesc() {
    if (totalCount === 0) return "宝贝真棒,继续加油哦!";
    const rate = correctCount / totalCount;
    if (rate === 1) return "宝贝太厉害了,全部拼写正确!";
    if (rate >= 0.8) return "宝贝很棒,拼写正确率很高!";
    if (rate >= 0.5) return "有进步空间,继续练习会更棒!";
    return "不要灰心,多练习就会进步!";
  }

  return (
    <div className="min-h-screen from-blue-100 to-pink-100 flex flex-col items-center justify-center relative px-2">
      {/* 右上角单词库切换 */}
      <div className="absolute top-4 right-4 z-10">
        <select
          className="rounded-lg border-2 border-pink-300 bg-white px-3 py-1 text-lg font-bold text-pink-600 shadow focus:outline-none focus:ring-2 focus:ring-pink-400"
          value={bankKey}
          onChange={handleBankChange}
        >
          {Object.entries(wordLibraries).map(([key, lib]) => (
            <option key={key} value={key}>
              {lib.name || key}
            </option>
          ))}
        </select>
      </div>
      {/* 标题 */}
      <h2 className="text-3xl md:text-4xl font-extrabold text-pink-500 drop-shadow mb-2 mt-2 select-none">
        快乐记单词
      </h2>
      {/* 中文意思,点击发音 */}
      <div
        className="text-2xl md:text-3xl font-bold text-blue-700 bg-yellow-100 rounded-xl px-6 py-3 mb-4 shadow cursor-pointer hover:bg-yellow-200 transition select-none mt-10 flex items-center gap-3 justify-center"
        title="点击播放英文发音"
        onClick={() => {
          if (window.speechSynthesis) {
            const utter = new window.SpeechSynthesisUtterance(
              currentWord.english
            );
            utter.lang = "en-US";
            window.speechSynthesis.speak(utter);
          }
        }}
      >
        {/* 扬声器图标 */}
        <svg
          xmlns="http://www.w3.org/2000/svg"
          className="w-7 h-7 md:w-8 md:h-8 text-blue-400"
          fill="none"
          viewBox="0 0 24 24"
          stroke="currentColor"
          strokeWidth="2"
        >
          <path
            strokeLinecap="round"
            strokeLinejoin="round"
            d="M9 9v6h4l5 5V4l-5 5H9z"
          />
        </svg>
        <span>“{currentWord.chinese}”</span>
      </div>
      {/* 拼写区 */}
      <div className="flex justify-center min-h-[48px] mb-8">
        {input.length > 0 && (
          <span
            className="bg-gradient-to-r from-pink-200 to-blue-200 text-pink-700 font-extrabold rounded-xl px-8 py-2 text-2xl shadow select-none tracking-wider cursor-pointer hover:bg-pink-300 transition-all"
            title="点击可清空"
            onClick={() => setInput([])}
          >
            {input.map((i) => i.letter).join("")}
          </span>
        )}
      </div>
      {/* 字母按钮区 */}
      <div className="flex flex-wrap justify-center gap-4 mb-10">
        {shuffled.map((letter, idx) => (
          <button
            key={idx}
            onClick={() =>
              !usedIdxs.includes(idx) && handleLetterClick(letter, idx)
            }
            disabled={usedIdxs.includes(idx)}
            className={`rounded-full px-5 py-3 text-2xl font-extrabold shadow-lg border-2 border-blue-200 transition-all duration-150 select-none
              ${
                usedIdxs.includes(idx)
                  ? "bg-gray-200 text-gray-400 border-gray-200 cursor-not-allowed"
                  : "bg-blue-200 text-blue-700 hover:bg-blue-300 hover:scale-110 cursor-pointer"
              }
            `}
          >
            {letter}
          </button>
        ))}
      </div>
      {/* 操作按钮区 */}
      <div className="flex gap-8 mb-8">
        <button
          onClick={handleSubmit}
          className="bg-green-400 hover:bg-green-500 text-white font-bold py-2 px-8 rounded-xl text-lg shadow transition-all disabled:bg-gray-300 disabled:cursor-not-allowed"
          disabled={input.length !== currentWord.english.length}
        >
          验证
        </button>
        <button
          onClick={handleNext}
          className="bg-yellow-400 hover:bg-yellow-500 text-white font-bold py-2 px-8 rounded-xl text-lg shadow transition-all"
        >
          下一个
        </button>
      </div>
      {/* 反馈信息 */}
      <div className="min-h-[32px] text-xl font-bold text-center">
        {feedback && (
          <span
            className={
              feedback.includes("恭喜")
                ? "text-green-600 animate-sparkle"
                : "text-red-500 animate-shake"
            }
          >
            {feedback}
          </span>
        )}
      </div>
      {/* 底部可爱装饰 */}
      <div className="absolute bottom-2 left-1/2 -translate-x-1/2 flex gap-2 opacity-70 pointer-events-none select-none">
        <span className="text-3xl animate-float">🍓</span>
        <span className="text-3xl animate-float">🍉</span>
        <span className="text-3xl animate-float">🍌</span>
        <span className="text-3xl animate-float">🦁</span>
        <span className="text-3xl animate-float">🐯</span>
        <span className="text-3xl animate-float">🐘</span>
      </div>
      {/* 倒计时弹窗 */}
      {showCountdown && (
        <div className="fixed inset-0 flex items-center justify-center z-50">
          <div className="relative flex items-center justify-center">
            {/* 转圈动画圆 */}
            <svg
              className="absolute w-40 h-40 animate-spin-slow"
              viewBox="0 0 160 160"
            >
              <circle
                cx="80"
                cy="80"
                r="70"
                fill="none"
                stroke="#2226"
                strokeWidth="18"
                strokeDasharray="330"
                strokeDashoffset="80"
                strokeLinecap="round"
              />
            </svg>
            {/* 半透明黑色圆背景 */}
            <div className="w-36 h-36 rounded-full bg-black bg-opacity-70 flex items-center justify-center">
              <span
                className="text-white text-7xl font-extrabold select-none"
                style={{ fontVariantNumeric: "tabular-nums" }}
              >
                {countdown}
              </span>
            </div>
          </div>
        </div>
      )}
      {/* 结束弹窗 */}
      {showFinish && (
        <div className="fixed inset-0 flex items-center justify-center z-50 bg-black bg-opacity-40">
          <div className="bg-white rounded-2xl shadow-2xl px-10 py-10 flex flex-col items-center">
            <div className="text-6xl mb-4">👏🎉</div>
            <div className="text-2xl font-bold text-pink-500 mb-2">
              {getFinishTitle()}
            </div>
            <div className="text-lg text-blue-600 mb-2">
              本轮正确率:
              {totalCount > 0
                ? Math.round((correctCount / totalCount) * 100)
                : 100}%
            </div>
            <div className="text-lg text-blue-600 mb-6">{getFinishDesc()}</div>
            <button
              onClick={handleReset}
              className="bg-pink-400 hover:bg-pink-500 text-white font-bold py-2 px-8 rounded-xl text-lg shadow transition-all"
            >
              再来一轮
            </button>
          </div>
        </div>
      )}
    </div>
  );
}

React 快乐记单词——儿童英语拼写游戏 - 高质量源码分享平台-免费下载各类网站源码与模板及前沿动态资讯


网站公告

今日签到

点亮在社区的每一天
去签到