文件结构
文件夹
word.html
data.js
html
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>考研英语单词背诵</title>
<script src="data.js"></script>
<style>
/* ===== 全局重置 & 基础 ===== */
* {
margin: 0;
padding: 0;
box-sizing: border-box;
}
body {
font-family: 'Segoe UI', 'PingFang SC', Roboto, 'Helvetica Neue', sans-serif;
background: #eef5ff;
min-height: 100vh;
display: flex;
justify-content: center;
align-items: center;
padding: 24px;
}
.app-container {
width: 1400px;
max-width: 100%;
background: #ffffff;
border-radius: 32px;
box-shadow: 0 20px 60px rgba(44, 107, 255, 0.15);
overflow: hidden;
display: flex;
flex-direction: column;
}
/* ===== 顶部栏 ===== */
.header {
background: linear-gradient(135deg, #2b6cff 0%, #1a4fc3 100%);
padding: 20px 36px;
display: flex;
justify-content: space-between;
align-items: center;
flex-wrap: wrap;
gap: 12px;
}
.header-left {
display: flex;
align-items: center;
gap: 16px;
}
.header-left h1 {
color: #fff;
font-size: 26px;
font-weight: 700;
letter-spacing: 0.5px;
}
.header-left .badge {
background: rgba(255, 255, 255, 0.2);
color: #fff;
padding: 4px 14px;
border-radius: 20px;
font-size: 14px;
font-weight: 500;
backdrop-filter: blur(4px);
}
.header-right {
display: flex;
align-items: center;
gap: 24px;
color: rgba(255, 255, 255, 0.9);
font-size: 15px;
}
.header-right .progress-text {
background: rgba(255, 255, 255, 0.15);
padding: 6px 18px;
border-radius: 20px;
font-weight: 500;
}
.header-right .mastery-count {
background: rgba(255, 255, 255, 0.15);
padding: 6px 18px;
border-radius: 20px;
font-weight: 500;
}
/* ===== 主体 ===== */
.main-area {
display: flex;
flex: 1;
min-height: 600px;
}
/* ===== 学习区 ===== */
.learning-area {
flex: 1;
padding: 40px 44px 32px;
display: flex;
flex-direction: column;
background: #fafcff;
border-right: 1px solid #e8eff9;
min-width: 0;
}
.word-display {
text-align: center;
padding: 8px 0 20px;
}
.word-display .part-of-speech {
font-size: 22px;
font-weight: 600;
color: #2b6cff;
background: #e8f0ff;
padding: 4px 18px;
border-radius: 30px;
display: inline-block;
margin-bottom: 8px;
}
.word-display .meaning {
font-size: 38px;
font-weight: 700;
color: #1a2a4a;
display: block;
margin-top: 4px;
letter-spacing: 1px;
}
/* 字母输入区 */
.input-area {
display: flex;
flex-direction: column;
align-items: center;
margin: 16px 0 8px;
padding: 20px 0;
position: relative;
}
.letter-container {
display: flex;
gap: 10px;
flex-wrap: wrap;
justify-content: center;
padding: 12px 20px;
background: #f5f9ff;
border-radius: 20px;
border: 2px dashed #c8daf5;
min-height: 80px;
align-items: center;
cursor: text;
transition: border-color 0.25s, background 0.25s;
width: 100%;
max-width: 700px;
}
.letter-container:focus-within {
border-color: #2b6cff;
background: #f0f6ff;
box-shadow: 0 0 0 4px rgba(43, 108, 255, 0.08);
}
.letter-container .letter-slot {
display: inline-flex;
justify-content: center;
align-items: center;
width: 44px;
height: 56px;
font-size: 30px;
font-weight: 700;
color: #1a2a4a;
background: #ffffff;
border-radius: 10px;
border: 2px solid #d4e4ff;
box-shadow: 0 2px 6px rgba(0, 0, 0, 0.02);
transition: all 0.15s;
font-family: 'Courier New', monospace;
text-transform: lowercase;
}
.letter-container .letter-slot.filled {
border-color: #2b6cff;
background: #eaf2ff;
color: #1a3a8a;
}
.letter-container .letter-slot.correct-slot {
border-color: #22c55e;
background: #e6f9ec;
color: #16a34a;
}
.letter-container .letter-slot.wrong-slot {
border-color: #ef4444;
background: #fee9e9;
color: #dc2626;
animation: shake 0.3s ease;
}
@keyframes shake {
0%,
100% {
transform: translateX(0);
}
25% {
transform: translateX(-6px);
}
75% {
transform: translateX(6px);
}
}
#hiddenInput {
position: absolute;
opacity: 0;
width: 0;
height: 0;
pointer-events: none;
}
.input-hint {
margin-top: 12px;
font-size: 14px;
color: #8899bb;
letter-spacing: 0.3px;
user-select: none;
}
.input-hint kbd {
background: #eef3fc;
padding: 2px 10px;
border-radius: 6px;
font-size: 13px;
border: 1px solid #d4e0f0;
color: #2b4a7a;
font-weight: 600;
}
/* 例句区域 */
.example-area {
margin: 12px 0 16px;
padding: 16px 24px;
background: #f5f9ff;
border-radius: 16px;
border-left: 5px solid #2b6cff;
min-height: 70px;
display: flex;
flex-direction: column;
justify-content: center;
transition: all 0.3s;
opacity: 0;
transform: translateY(8px);
max-width: 700px;
align-self: center;
width: 100%;
}
.example-area.visible {
opacity: 1;
transform: translateY(0);
}
.example-area .example-en {
font-size: 18px;
color: #1a2a4a;
font-weight: 500;
line-height: 1.6;
}
.example-area .example-zh {
font-size: 16px;
color: #5a6f8a;
margin-top: 4px;
line-height: 1.5;
}
.example-area .example-label {
font-size: 13px;
color: #2b6cff;
font-weight: 600;
margin-bottom: 4px;
letter-spacing: 0.5px;
}
/* 控制按钮 */
.controls {
display: flex;
flex-wrap: wrap;
justify-content: center;
gap: 12px;
margin-top: 20px;
padding-top: 16px;
border-top: 1px solid #e8eff9;
}
.controls button {
padding: 10px 24px;
border: none;
border-radius: 30px;
font-size: 15px;
font-weight: 600;
cursor: pointer;
transition: all 0.2s;
display: inline-flex;
align-items: center;
gap: 6px;
background: #f0f6ff;
color: #1a3a6b;
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.02);
}
.controls button:hover {
transform: translateY(-2px);
box-shadow: 0 8px 20px rgba(43, 108, 255, 0.15);
}
.controls button:active {
transform: scale(0.96);
}
.controls .btn-primary {
background: #2b6cff;
color: #fff;
}
.controls .btn-primary:hover {
background: #1a5ae0;
box-shadow: 0 8px 24px rgba(43, 108, 255, 0.30);
}
.controls .btn-outline {
background: transparent;
border: 2px solid #c8daf5;
color: #2b4a7a;
}
.controls .btn-outline:hover {
border-color: #2b6cff;
background: #f0f6ff;
}
.controls .btn-warning {
background: #f59e0b;
color: #fff;
}
.controls .btn-warning:hover {
background: #d97706;
box-shadow: 0 8px 24px rgba(245, 158, 11, 0.30);
}
.controls .btn-success {
background: #22c55e;
color: #fff;
}
.controls .btn-success:hover {
background: #16a34a;
box-shadow: 0 8px 24px rgba(34, 197, 94, 0.30);
}
/* ===== 单词列表 ===== */
.word-list-area {
width: 320px;
min-width: 260px;
background: #fafcff;
display: flex;
flex-direction: column;
padding: 20px 16px 16px;
flex-shrink: 0;
}
.word-list-header {
display: flex;
justify-content: space-between;
align-items: center;
padding-bottom: 12px;
border-bottom: 2px solid #e8eff9;
margin-bottom: 12px;
flex-shrink: 0;
}
.word-list-header .list-title {
font-size: 17px;
font-weight: 700;
color: #1a2a4a;
display: flex;
align-items: center;
gap: 8px;
}
.word-list-header .list-title span {
background: #2b6cff;
color: #fff;
font-size: 12px;
padding: 1px 10px;
border-radius: 20px;
font-weight: 600;
}
.word-list-header .page-info {
font-size: 14px;
color: #6a7f9a;
font-weight: 500;
}
.word-list-body {
flex: 1;
overflow-y: auto;
padding-right: 4px;
min-height: 0;
}
.word-list-body::-webkit-scrollbar {
width: 4px;
}
.word-list-body::-webkit-scrollbar-track {
background: #eef3fc;
border-radius: 10px;
}
.word-list-body::-webkit-scrollbar-thumb {
background: #c8daf5;
border-radius: 10px;
}
.word-item {
display: flex;
justify-content: space-between;
align-items: center;
padding: 8px 14px;
margin-bottom: 4px;
border-radius: 12px;
font-size: 15px;
font-weight: 500;
color: #1a2a4a;
transition: all 0.15s;
cursor: default;
border: 1px solid transparent;
}
.word-item .word-text {
font-weight: 600;
font-size: 16px;
color: #1a2a4a;
}
.word-item .word-status {
font-size: 14px;
opacity: 0.7;
}
.word-item.current {
background: #e8f0ff;
border-color: #2b6cff;
box-shadow: 0 2px 8px rgba(43, 108, 255, 0.08);
}
.word-item.current .word-text {
color: #1a4fc3;
}
.word-item.mastered {
background: #e6f9ec;
border-color: #86d9a4;
}
.word-item.mastered .word-text {
color: #16a34a;
}
.word-item.mastered .word-status {
color: #22c55e;
}
.word-list-footer {
display: flex;
justify-content: space-between;
gap: 8px;
padding-top: 12px;
border-top: 2px solid #e8eff9;
margin-top: 12px;
flex-shrink: 0;
}
.word-list-footer button {
flex: 1;
padding: 8px 0;
border: none;
border-radius: 30px;
font-size: 14px;
font-weight: 600;
cursor: pointer;
background: #f0f6ff;
color: #1a3a6b;
transition: all 0.2s;
}
.word-list-footer button:hover {
background: #e0ebff;
transform: translateY(-1px);
}
.word-list-footer button:active {
transform: scale(0.96);
}
.word-list-footer button:disabled {
opacity: 0.4;
cursor: not-allowed;
transform: none;
}
/* ===== 完成模态框 ===== */
.completion-overlay {
position: fixed;
top: 0;
left: 0;
width: 100%;
height: 100%;
background: rgba(0, 0, 0, 0.5);
backdrop-filter: blur(8px);
display: none;
justify-content: center;
align-items: center;
z-index: 9999;
animation: fadeInOverlay 0.5s ease;
}
.completion-overlay.active {
display: flex;
}
@keyframes fadeInOverlay {
from {
opacity: 0;
}
to {
opacity: 1;
}
}
.completion-card {
background: linear-gradient(135deg, #ffffff 0%, #f0f7ff 100%);
border-radius: 48px;
padding: 56px 64px;
max-width: 550px;
width: 90%;
text-align: center;
box-shadow: 0 40px 80px rgba(43, 108, 255, 0.3);
position: relative;
animation: popIn 0.6s cubic-bezier(0.175, 0.885, 0.32, 1.275);
border: 2px solid rgba(43, 108, 255, 0.2);
}
@keyframes popIn {
0% {
transform: scale(0.5);
opacity: 0;
}
100% {
transform: scale(1);
opacity: 1;
}
}
.completion-card .emoji-big {
font-size: 72px;
display: block;
margin-bottom: 8px;
}
.completion-card h2 {
font-size: 34px;
color: #1a2a4a;
margin: 16px 0 8px;
font-weight: 800;
}
.completion-card p {
font-size: 18px;
color: #4a5f7a;
margin: 8px 0 24px;
line-height: 1.6;
}
.completion-card .btn-celebrate {
background: linear-gradient(135deg, #2b6cff, #1a4fc3);
color: #fff;
border: none;
padding: 14px 44px;
border-radius: 60px;
font-size: 18px;
font-weight: 700;
cursor: pointer;
transition: all 0.3s;
box-shadow: 0 10px 30px rgba(43, 108, 255, 0.3);
}
.completion-card .btn-celebrate:hover {
transform: translateY(-3px) scale(1.02);
box-shadow: 0 16px 40px rgba(43, 108, 255, 0.4);
}
/* ===== 五彩纸屑 ===== */
.confetti-container {
position: fixed;
top: 0;
left: 0;
width: 100%;
height: 100%;
pointer-events: none;
z-index: 10000;
overflow: hidden;
}
.confetti-piece {
position: absolute;
width: 12px;
height: 12px;
opacity: 0.9;
animation: confettiFall linear forwards;
}
@keyframes confettiFall {
0% {
transform: translateY(-20vh) rotate(0deg) scale(1);
opacity: 1;
}
100% {
transform: translateY(110vh) rotate(720deg) scale(0.5);
opacity: 0;
}
}
/* ===== 响应式 ===== */
@media (max-width: 1024px) {
.main-area {
flex-direction: column;
}
.word-list-area {
width: 100%;
min-width: 0;
border-top: 1px solid #e8eff9;
max-height: 280px;
}
.learning-area {
border-right: none;
padding: 28px 24px 20px;
}
.letter-container .letter-slot {
width: 36px;
height: 48px;
font-size: 24px;
}
.word-display .meaning {
font-size: 30px;
}
}
@media (max-width: 600px) {
.header {
padding: 14px 18px;
}
.header-left h1 {
font-size: 20px;
}
.header-right .progress-text,
.header-right .mastery-count {
font-size: 12px;
padding: 4px 12px;
}
.learning-area {
padding: 20px 16px 16px;
}
.word-display .meaning {
font-size: 26px;
}
.word-display .part-of-speech {
font-size: 18px;
}
.letter-container {
gap: 6px;
padding: 10px 12px;
}
.letter-container .letter-slot {
width: 30px;
height: 40px;
font-size: 20px;
}
.controls button {
padding: 8px 16px;
font-size: 13px;
}
.example-area .example-en {
font-size: 16px;
}
.word-list-area {
max-height: 200px;
padding: 12px 12px 12px;
}
.word-item {
font-size: 13px;
padding: 6px 10px;
}
.completion-card {
padding: 40px 28px;
}
.completion-card h2 {
font-size: 28px;
}
}
.fade-in {
animation: fadeIn 0.3s ease;
}
@keyframes fadeIn {
from {
opacity: 0;
transform: translateY(10px);
}
to {
opacity: 1;
transform: translateY(0);
}
}
.celebration {
animation: pop 0.4s ease;
}
@keyframes pop {
0% {
transform: scale(0.9);
opacity: 0.5;
}
50% {
transform: scale(1.05);
}
100% {
transform: scale(1);
opacity: 1;
}
}
</style>
</head>
<body>
<div class="app-container">
<header class="header">
<div class="header-left">
<h1>📖 考研英语</h1>
<span class="badge">单词背诵</span>
</div>
<div class="header-right">
<span class="progress-text" id="progressText">第 1 / 22 个</span>
<span class="mastery-count" id="masteryCount">✅ 已掌握 0</span>
</div>
</header>
<div class="main-area">
<!-- 学习区 -->
<section class="learning-area">
<div class="word-display">
<span class="part-of-speech" id="partOfSpeech">v.</span>
<span class="meaning" id="meaning">放弃,遗弃</span>
</div>
<div class="input-area">
<div class="letter-container" id="letterContainer" tabindex="0">
<!-- 由 JS 动态生成 -->
</div>
<input type="text" id="hiddenInput" autocomplete="off" autocorrect="off" autocapitalize="off" spellcheck="false" />
<div class="input-hint">
输入单词 · <kbd>Enter</kbd> 确认 · <kbd>⌫</kbd> 删除
</div>
</div>
<div class="example-area" id="exampleArea">
<div class="example-label">📌 例句</div>
<div class="example-en" id="exampleEn">He had to abandon his car in the snow.</div>
<div class="example-zh" id="exampleZh">他不得不把车丢弃在雪地里。</div>
</div>
<div class="controls">
<button class="btn-outline" id="prevBtn">◀ 上一个</button>
<button class="btn-outline" id="nextBtn">下一个 ▶</button>
<button class="btn-success" id="showAnswerBtn">👁️ 显示答案</button>
<button class="btn-warning" id="resetBtn">🔄 重新开始</button>
<button class="btn-primary" id="shuffleBtn">🎲 打乱顺序</button>
</div>
</section>
<!-- 单词列表 -->
<aside class="word-list-area">
<div class="word-list-header">
<div class="list-title">
📋 单词一览
<span id="totalCount">22</span>
</div>
<span class="page-info" id="pageInfo">1 / 3</span>
</div>
<div class="word-list-body" id="wordListBody">
<!-- 由 JS 动态生成 -->
</div>
<div class="word-list-footer">
<button id="prevPageBtn">◀ 上一页</button>
<button id="nextPageBtn">下一页 ▶</button>
</div>
</aside>
</div>
</div>
<!-- ===== 完成模态框 ===== -->
<div class="completion-overlay" id="completionOverlay">
<div class="completion-card">
<span class="emoji-big">🎉</span>
<h2>太棒了!全部完成!</h2>
<p>你已掌握所有 <strong id="totalWordsDisplay">22</strong> 个考研单词,<br />继续加油,胜利就在前方!</p>
<button class="btn-celebrate" id="celebrationRestartBtn">🚀 重新挑战</button>
</div>
</div>
<div class="confetti-container" id="confettiContainer"></div>
<script>
// ================================================================
// 主逻辑
// ================================================================
const allWords = window.wordData || [];
const state = {
words: [],
currentIndex: 0,
currentInput: [],
isCorrect: false,
mastered: new Set(),
pageSize: 10, // ✅ 已改为每页10个
currentPage: 0,
totalPages: 1,
isWaitingForNext: false,
isShuffled: false,
completionShown: false,
showingAnswer: false,
};
// ---------- DOM ----------
const $ = (id) => document.getElementById(id);
const letterContainer = $('letterContainer');
const hiddenInput = $('hiddenInput');
const partOfSpeech = $('partOfSpeech');
const meaning = $('meaning');
const exampleArea = $('exampleArea');
const exampleEn = $('exampleEn');
const exampleZh = $('exampleZh');
const progressText = $('progressText');
const masteryCount = $('masteryCount');
const wordListBody = $('wordListBody');
const pageInfo = $('pageInfo');
const totalCount = $('totalCount');
const prevBtn = $('prevBtn');
const nextBtn = $('nextBtn');
const resetBtn = $('resetBtn');
const shuffleBtn = $('shuffleBtn');
const showAnswerBtn = $('showAnswerBtn');
const prevPageBtn = $('prevPageBtn');
const nextPageBtn = $('nextPageBtn');
const completionOverlay = $('completionOverlay');
const totalWordsDisplay = $('totalWordsDisplay');
const celebrationRestartBtn = $('celebrationRestartBtn');
const confettiContainer = $('confettiContainer');
// ---------- 初始化 ----------
function init() {
if (!allWords.length) {
letterContainer.innerHTML = `<div class="empty-state">⚠️ 请确保 data.js 已加载,且包含有效的单词数据。</div>`;
return;
}
state.words = [...allWords];
state.totalPages = Math.ceil(state.words.length / state.pageSize);
totalCount.textContent = state.words.length;
totalWordsDisplay.textContent = state.words.length;
state.currentIndex = 0;
state.mastered = new Set();
state.isShuffled = false;
state.isWaitingForNext = false;
state.currentPage = 0;
state.completionShown = false;
state.showingAnswer = false;
completionOverlay.classList.remove('active');
confettiContainer.innerHTML = '';
renderAll();
focusInput();
}
// ---------- 渲染 ----------
function renderAll() {
renderWord();
renderLetterSlots();
renderWordList();
renderPageInfo();
renderProgress();
renderMastery();
}
function renderWord() {
const word = getCurrentWord();
if (!word) return;
partOfSpeech.textContent = word.partOfSpeech || '';
meaning.textContent = word.meaning || '';
if (state.isCorrect || state.showingAnswer) {
exampleEn.textContent = word.example || '';
exampleZh.textContent = word.exampleTranslation || '';
exampleArea.classList.add('visible');
} else {
exampleArea.classList.remove('visible');
}
}
function renderLetterSlots() {
const word = getCurrentWord();
if (!word) {
letterContainer.innerHTML = '';
return;
}
const wordLen = word.word.length;
const input = state.currentInput;
const isCorrect = state.isCorrect;
const showing = state.showingAnswer;
let html = '';
for (let i = 0; i < wordLen; i++) {
let char = '';
let cls = 'letter-slot';
if (showing) {
char = word.word[i];
cls += ' correct-slot filled';
} else if (isCorrect) {
char = word.word[i];
cls += ' correct-slot filled';
} else {
char = input[i] || '';
if (char) cls += ' filled';
}
html += `<span class="${cls}" data-index="${i}">${char || '_'}</span>`;
}
letterContainer.innerHTML = html;
}
function renderWordList() {
const start = state.currentPage * state.pageSize;
const end = Math.min(start + state.pageSize, state.words.length);
const pageWords = state.words.slice(start, end);
const currentIdx = state.currentIndex;
if (!pageWords.length) {
wordListBody.innerHTML = `<div class="empty-state">暂无单词</div>`;
return;
}
let html = '';
pageWords.forEach((w, idx) => {
const globalIdx = start + idx;
const isCurrent = globalIdx === currentIdx;
const isMastered = state.mastered.has(globalIdx);
let cls = 'word-item';
if (isCurrent) cls += ' current';
if (isMastered) cls += ' mastered';
const statusIcon = isMastered ? '✅' : '○';
html += `
<div class="${cls}" data-index="${globalIdx}">
<span class="word-text">${globalIdx + 1}</span>
<span class="word-status">${statusIcon}</span>
</div>
`;
});
wordListBody.innerHTML = html;
wordListBody.querySelectorAll('.word-item').forEach((el) => {
el.addEventListener('click', () => {
const idx = parseInt(el.dataset.index, 10);
if (!isNaN(idx) && idx !== state.currentIndex) {
goToWord(idx);
}
});
});
}
function renderPageInfo() {
const total = state.totalPages || 1;
const current = state.currentPage + 1;
pageInfo.textContent = `${current} / ${total}`;
prevPageBtn.disabled = state.currentPage === 0;
nextPageBtn.disabled = state.currentPage >= total - 1;
}
function renderProgress() {
const total = state.words.length;
const current = state.currentIndex + 1;
progressText.textContent = `第 ${current} / ${total} 个`;
}
function renderMastery() {
masteryCount.textContent = `✅ 已掌握 ${state.mastered.size}`;
}
// ---------- 工具 ----------
function getCurrentWord() {
return state.words[state.currentIndex] || null;
}
function focusInput() {
hiddenInput.focus();
}
function goToWord(index) {
if (index < 0 || index >= state.words.length) return;
state.currentIndex = index;
state.currentInput = [];
state.isCorrect = false;
state.isWaitingForNext = false;
state.showingAnswer = false;
const page = Math.floor(index / state.pageSize);
if (page !== state.currentPage) {
state.currentPage = page;
}
renderAll();
focusInput();
}
// ---------- 检查答案 ----------
function checkAnswer() {
const word = getCurrentWord();
if (!word) return;
const userInput = state.currentInput.join('');
const target = word.word.toLowerCase();
if (userInput.toLowerCase() === target) {
state.isCorrect = true;
state.isWaitingForNext = true;
state.mastered.add(state.currentIndex);
state.showingAnswer = false;
renderAll();
letterContainer.classList.add('celebration');
setTimeout(() => letterContainer.classList.remove('celebration'), 400);
focusInput();
if (state.mastered.size === state.words.length && !state.completionShown) {
state.completionShown = true;
showCompletionModal();
}
} else {
state.currentInput = [];
state.isCorrect = false;
state.showingAnswer = false;
renderAll();
const slots = letterContainer.querySelectorAll('.letter-slot');
slots.forEach((slot) => {
slot.classList.add('wrong-slot');
setTimeout(() => slot.classList.remove('wrong-slot'), 400);
});
focusInput();
}
}
// ---------- 显示答案 ----------
function showAnswer() {
const word = getCurrentWord();
if (!word) return;
if (state.showingAnswer || state.isCorrect) return;
state.showingAnswer = true;
state.currentInput = [];
renderAll();
letterContainer.classList.add('celebration');
setTimeout(() => letterContainer.classList.remove('celebration'), 400);
focusInput();
}
// ---------- 完成模态框 ----------
function showCompletionModal() {
completionOverlay.classList.add('active');
launchConfetti();
}
function launchConfetti() {
const container = confettiContainer;
container.innerHTML = '';
const colors = ['#2b6cff', '#22c55e', '#f59e0b', '#ef4444', '#8b5cf6', '#ec4899', '#14b8a6', '#f97316'];
const count = 80;
for (let i = 0; i < count; i++) {
const piece = document.createElement('div');
piece.className = 'confetti-piece';
const size = 6 + Math.random() * 12;
const isCircle = Math.random() > 0.5;
piece.style.width = isCircle ? size + 'px' : size * 0.6 + 'px';
piece.style.height = isCircle ? size + 'px' : size * 1.2 + 'px';
piece.style.borderRadius = isCircle ? '50%' : '2px';
piece.style.background = colors[Math.floor(Math.random() * colors.length)];
piece.style.left = Math.random() * 100 + '%';
piece.style.animationDuration = 2 + Math.random() * 3 + 's';
piece.style.animationDelay = Math.random() * 2 + 's';
piece.style.transform = `rotate(${Math.random() * 360}deg)`;
container.appendChild(piece);
}
setTimeout(() => {
container.innerHTML = '';
}, 5000);
}
// ---------- 导航 ----------
function goToNextWord() {
if (state.currentIndex < state.words.length - 1) {
goToWord(state.currentIndex + 1);
} else {
goToWord(0);
}
}
function goToPrevWord() {
if (state.currentIndex > 0) {
goToWord(state.currentIndex - 1);
} else {
goToWord(state.words.length - 1);
}
}
function resetAll() {
state.currentIndex = 0;
state.currentInput = [];
state.isCorrect = false;
state.isWaitingForNext = false;
state.mastered = new Set();
state.currentPage = 0;
state.completionShown = false;
state.showingAnswer = false;
completionOverlay.classList.remove('active');
confettiContainer.innerHTML = '';
if (state.isShuffled) {
state.words = [...allWords];
state.isShuffled = false;
}
renderAll();
focusInput();
}
function shuffleWords() {
const arr = [...state.words];
for (let i = arr.length - 1; i > 0; i--) {
const j = Math.floor(Math.random() * (i + 1));
[arr[i], arr[j]] = [arr[j], arr[i]];
}
state.words = arr;
state.isShuffled = true;
state.currentIndex = 0;
state.currentInput = [];
state.isCorrect = false;
state.isWaitingForNext = false;
state.mastered = new Set();
state.currentPage = 0;
state.completionShown = false;
state.showingAnswer = false;
completionOverlay.classList.remove('active');
confettiContainer.innerHTML = '';
renderAll();
focusInput();
}
// ---------- 键盘事件 ----------
hiddenInput.addEventListener('keydown', (e) => {
const word = getCurrentWord();
if (!word) return;
if (state.showingAnswer) {
state.showingAnswer = false;
state.currentInput = [];
state.isCorrect = false;
exampleArea.classList.remove('visible');
renderAll();
if (e.key.length === 1 && /[a-zA-Z]/.test(e.key)) {
e.preventDefault();
state.currentInput.push(e.key.toLowerCase());
renderLetterSlots();
focusInput();
return;
} else if (e.key === 'Enter' || e.key === 'Backspace') {
e.preventDefault();
focusInput();
return;
}
return;
}
if (state.isWaitingForNext) {
if (e.key === 'Enter') {
e.preventDefault();
state.isWaitingForNext = false;
state.isCorrect = false;
state.currentInput = [];
goToNextWord();
}
return;
}
if (e.key === 'Backspace') {
e.preventDefault();
if (state.currentInput.length > 0) {
state.currentInput.pop();
renderLetterSlots();
if (state.isCorrect) {
state.isCorrect = false;
exampleArea.classList.remove('visible');
renderWord();
}
}
return;
}
if (e.key === 'Enter') {
e.preventDefault();
if (state.currentInput.length === word.word.length) {
checkAnswer();
} else {
state.currentInput = [];
renderLetterSlots();
focusInput();
}
return;
}
if (e.key.length === 1 && /[a-zA-Z]/.test(e.key)) {
e.preventDefault();
if (state.isCorrect) return;
if (state.currentInput.length < word.word.length) {
state.currentInput.push(e.key.toLowerCase());
renderLetterSlots();
}
}
});
letterContainer.addEventListener('click', () => {
focusInput();
});
document.addEventListener('click', (e) => {
if (e.target.closest('.app-container')) {
if (!e.target.closest('input') && !e.target.closest('button')) {
focusInput();
}
}
});
// ---------- 按钮绑定 ----------
prevBtn.addEventListener('click', goToPrevWord);
nextBtn.addEventListener('click', () => {
if (state.isWaitingForNext) {
state.isWaitingForNext = false;
state.isCorrect = false;
state.currentInput = [];
goToNextWord();
} else {
goToNextWord();
}
});
showAnswerBtn.addEventListener('click', showAnswer);
resetBtn.addEventListener('click', resetAll);
shuffleBtn.addEventListener('click', shuffleWords);
prevPageBtn.addEventListener('click', () => {
if (state.currentPage > 0) {
state.currentPage--;
renderWordList();
renderPageInfo();
const start = state.currentPage * state.pageSize;
const end = start + state.pageSize;
if (state.currentIndex < start || state.currentIndex >= end) {
goToWord(start);
} else {
renderAll();
}
}
});
nextPageBtn.addEventListener('click', () => {
const total = state.totalPages || 1;
if (state.currentPage < total - 1) {
state.currentPage++;
renderWordList();
renderPageInfo();
const start = state.currentPage * state.pageSize;
const end = start + state.pageSize;
if (state.currentIndex < start || state.currentIndex >= end) {
goToWord(start);
} else {
renderAll();
}
}
});
celebrationRestartBtn.addEventListener('click', resetAll);
// ---------- 启动 ----------
document.addEventListener('DOMContentLoaded', () => {
if (typeof window.wordData === 'undefined') {
letterContainer.innerHTML = `
<div class="empty-state" style="padding:30px 0;">
⚠️ 未找到单词数据,请确保 data.js 已正确加载。<br />
<small style="color:#8899bb;">请将 data.js 放在同一目录下,并包含 window.wordData 数组。</small>
</div>
`;
return;
}
init();
});
window.addEventListener('focus', () => {
setTimeout(focusInput, 100);
});
console.log('📚 考研英语单词背诵已启动!');
console.log(`📝 共加载 ${allWords.length} 个单词。`);
</script>
</body>
</html>
javascript
// data.js
window.wordData = [
// ----- 新增 12 个考研词汇 -----
{
"word": "autonomous",
"partOfSpeech": "adj.",
"meaning": "自治的,自主的",
"example": "The region is an autonomous republic.",
"exampleTranslation": "该地区是一个自治共和国。"
},
{
"word": "colony",
"partOfSpeech": "n.",
"meaning": "殖民地,群体",
"example": "The former colony gained independence in 1960.",
"exampleTranslation": "这个前殖民地于1960年获得独立。"
},
{
"word": "diplomacy",
"partOfSpeech": "n.",
"meaning": "外交,策略",
"example": "Diplomacy is essential to avoid conflict.",
"exampleTranslation": "外交是避免冲突的关键。"
},
{
"word": "independent",
"partOfSpeech": "adj.",
"meaning": "独立的,自主的",
"example": "She is financially independent.",
"exampleTranslation": "她在经济上独立。"
},
{
"word": "self-reliant",
"partOfSpeech": "adj.",
"meaning": "自立的,自力更生的",
"example": "The community is self-reliant in food production.",
"exampleTranslation": "这个社区在粮食生产上自给自足。"
},
{
"word": "self-sufficient",
"partOfSpeech": "adj.",
"meaning": "自给自足的",
"example": "The country aims to be self-sufficient in energy.",
"exampleTranslation": "该国力求能源自给自足。"
},
{
"word": "sovereign",
"partOfSpeech": "adj./n.",
"meaning": "至高无上的,主权的;君主",
"example": "The nation is a sovereign state.",
"exampleTranslation": "该国是一个主权国家。"
},
{
"word": "reign",
"partOfSpeech": "v./n.",
"meaning": "统治,支配;统治时期",
"example": "The king reigned for over forty years.",
"exampleTranslation": "这位国王统治了四十多年。"
},
{
"word": "conservative",
"partOfSpeech": "adj.",
"meaning": "保守的,守旧的",
"example": "He holds conservative views on social issues.",
"exampleTranslation": "他在社会问题上持保守观点。"
},
{
"word": "democracy",
"partOfSpeech": "n.",
"meaning": "民主,民主制度",
"example": "Democracy allows citizens to vote.",
"exampleTranslation": "民主制度允许公民投票。"
},
{
"word": "liberal",
"partOfSpeech": "adj.",
"meaning": "自由的,开明的;自由主义的",
"example": "She has a liberal attitude toward education.",
"exampleTranslation": "她对教育持开明态度。"
},
{
"word": "republic",
"partOfSpeech": "n.",
"meaning": "共和国,共和政体",
"example": "France is a republic.",
"exampleTranslation": "法国是一个共和国。"
}
];