dramaling-vocab-learning/docs/02_design/component-library/pages/learning-page.html

824 lines
23 KiB
HTML
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

<!DOCTYPE html>
<html lang="zh-TW">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>詞彙學習 - Drama Ling</title>
<link rel="stylesheet" href="../../design-system/tokens/design-tokens.css">
<link rel="stylesheet" href="../assets/styles/base.css">
<link rel="stylesheet" href="../assets/styles/components.css">
<style>
body {
background: linear-gradient(135deg, var(--background-primary), var(--background-secondary));
margin: 0;
padding: 0;
min-height: 100vh;
}
/* 學習容器 */
.learning-container {
max-width: 800px;
margin: 0 auto;
padding: var(--space-4);
min-height: 100vh;
display: flex;
flex-direction: column;
}
/* 頂部狀態欄 */
.learning-header {
display: flex;
justify-content: space-between;
align-items: center;
padding: var(--space-4) 0;
margin-bottom: var(--space-6);
}
.header-left {
display: flex;
align-items: center;
gap: var(--space-4);
}
.back-button {
width: 40px;
height: 40px;
background: var(--card-background);
border: 1px solid var(--divider);
border-radius: var(--radius-full);
display: flex;
align-items: center;
justify-content: center;
cursor: pointer;
transition: all 0.3s ease;
text-decoration: none;
color: var(--text-primary);
}
.back-button:hover {
background: var(--primary-teal);
color: var(--background-dark);
transform: scale(1.1);
}
.level-info {
display: flex;
align-items: center;
gap: var(--space-2);
background: linear-gradient(135deg, var(--level-background), var(--secondary-purple-dark));
padding: var(--space-2) var(--space-4);
border-radius: var(--radius-full);
color: white;
font-weight: 600;
font-size: var(--text-sm);
}
.header-right {
display: flex;
align-items: center;
gap: var(--space-4);
}
/* 進度條容器 */
.progress-container {
background: var(--card-background);
border-radius: var(--radius-full);
padding: 4px;
margin-bottom: var(--space-6);
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.1);
}
.learning-progress {
height: 12px;
background: linear-gradient(90deg, var(--primary-teal), var(--accent-violet));
border-radius: var(--radius-full);
transition: width 0.6s ease;
position: relative;
overflow: hidden;
}
.learning-progress::after {
content: '';
position: absolute;
top: 0;
left: 0;
right: 0;
bottom: 0;
background: linear-gradient(90deg, transparent, rgba(255, 255, 255, 0.3), transparent);
animation: progressShimmer 2s infinite;
}
/* 學習卡片 */
.learning-card {
background: var(--card-background);
border-radius: var(--radius-2xl);
padding: var(--space-10);
box-shadow: 0 10px 40px rgba(0, 0, 0, 0.2);
border: 2px solid var(--divider);
margin-bottom: var(--space-6);
min-height: 400px;
display: flex;
flex-direction: column;
justify-content: center;
align-items: center;
position: relative;
overflow: hidden;
animation: cardSlideIn 0.5s ease-out;
}
@keyframes cardSlideIn {
from {
opacity: 0;
transform: translateY(30px) scale(0.95);
}
to {
opacity: 1;
transform: translateY(0) scale(1);
}
}
.learning-card::before {
content: '';
position: absolute;
top: -2px;
left: -2px;
right: -2px;
bottom: -2px;
background: linear-gradient(45deg, var(--primary-teal), var(--accent-violet), var(--secondary-purple));
border-radius: inherit;
opacity: 0;
z-index: -1;
transition: opacity 0.3s ease;
}
.learning-card:hover::before {
opacity: 1;
}
/* 詞彙展示 */
.word-display {
text-align: center;
margin-bottom: var(--space-8);
}
.word-main {
font-size: var(--text-4xl);
font-weight: 700;
color: var(--primary-teal);
margin-bottom: var(--space-4);
animation: wordPulse 2s ease-in-out infinite;
}
@keyframes wordPulse {
0%, 100% { transform: scale(1); }
50% { transform: scale(1.05); }
}
.word-pronunciation {
font-size: var(--text-lg);
color: var(--text-secondary);
margin-bottom: var(--space-2);
font-style: italic;
}
.word-translation {
font-size: var(--text-xl);
color: var(--text-primary);
margin-bottom: var(--space-4);
}
.word-example {
background: var(--background-secondary);
border-left: 3px solid var(--primary-teal);
padding: var(--space-4);
border-radius: var(--radius-lg);
text-align: left;
margin-top: var(--space-6);
}
.example-sentence {
font-size: var(--text-base);
color: var(--text-primary);
margin-bottom: var(--space-2);
line-height: 1.6;
}
.example-translation {
font-size: var(--text-sm);
color: var(--text-secondary);
font-style: italic;
}
/* 語音按鈕 */
.audio-button {
width: 60px;
height: 60px;
background: linear-gradient(135deg, var(--primary-teal), var(--primary-teal-light));
border: none;
border-radius: var(--radius-full);
color: var(--background-dark);
font-size: 24px;
cursor: pointer;
transition: all 0.3s ease;
display: flex;
align-items: center;
justify-content: center;
margin: 0 auto var(--space-6);
box-shadow: 0 4px 16px rgba(0, 229, 204, 0.3);
}
.audio-button:hover {
transform: scale(1.1);
box-shadow: 0 6px 24px rgba(0, 229, 204, 0.4);
}
.audio-button:active {
transform: scale(0.95);
}
.audio-button.playing {
animation: audioPlaying 1s ease-in-out infinite;
}
@keyframes audioPlaying {
0%, 100% { transform: scale(1); }
25% { transform: scale(1.1); }
75% { transform: scale(0.95); }
}
/* 選項按鈕 */
.options-container {
display: grid;
grid-template-columns: repeat(2, 1fr);
gap: var(--space-4);
margin-top: var(--space-6);
width: 100%;
}
.option-button {
padding: var(--space-4) var(--space-6);
background: var(--background-secondary);
border: 2px solid var(--divider);
border-radius: var(--radius-xl);
font-size: var(--text-base);
font-weight: 500;
color: var(--text-primary);
cursor: pointer;
transition: all 0.3s ease;
position: relative;
overflow: hidden;
}
.option-button:hover {
background: var(--card-background);
border-color: var(--primary-teal);
transform: translateY(-2px);
box-shadow: 0 4px 12px rgba(0, 229, 204, 0.2);
}
.option-button.correct {
background: linear-gradient(135deg, rgba(76, 175, 80, 0.1), rgba(76, 175, 80, 0.05));
border-color: var(--success-green);
color: var(--success-green);
animation: correctAnswer 0.5s ease;
}
.option-button.incorrect {
background: linear-gradient(135deg, rgba(231, 76, 60, 0.1), rgba(231, 76, 60, 0.05));
border-color: var(--error-red);
color: var(--error-red);
animation: shake 0.5s ease;
}
@keyframes correctAnswer {
0% { transform: scale(1); }
50% { transform: scale(1.05); }
100% { transform: scale(1); }
}
@keyframes shake {
0%, 100% { transform: translateX(0); }
10%, 30%, 50%, 70%, 90% { transform: translateX(-5px); }
20%, 40%, 60%, 80% { transform: translateX(5px); }
}
/* 底部操作區 */
.learning-footer {
margin-top: auto;
padding-top: var(--space-6);
}
.action-buttons {
display: flex;
gap: var(--space-4);
justify-content: center;
margin-bottom: var(--space-6);
}
.skip-button {
padding: var(--space-3) var(--space-6);
background: transparent;
border: 2px solid var(--text-tertiary);
border-radius: var(--radius-lg);
color: var(--text-tertiary);
font-weight: 600;
cursor: pointer;
transition: all 0.3s ease;
}
.skip-button:hover {
border-color: var(--text-secondary);
color: var(--text-secondary);
background: var(--card-background);
}
.continue-button {
padding: var(--space-3) var(--space-8);
background: linear-gradient(135deg, var(--primary-teal), var(--primary-teal-light));
border: none;
border-radius: var(--radius-lg);
color: var(--background-dark);
font-weight: 600;
font-size: var(--text-base);
cursor: pointer;
transition: all 0.3s ease;
box-shadow: 0 4px 16px rgba(0, 229, 204, 0.3);
}
.continue-button:hover {
transform: translateY(-2px);
box-shadow: 0 6px 24px rgba(0, 229, 204, 0.4);
}
.continue-button:disabled {
opacity: 0.5;
cursor: not-allowed;
transform: none;
}
/* 成就彈窗 */
.achievement-popup {
position: fixed;
top: 50%;
left: 50%;
transform: translate(-50%, -50%) scale(0);
background: var(--card-background);
border-radius: var(--radius-2xl);
padding: var(--space-8);
box-shadow: 0 20px 60px rgba(0, 0, 0, 0.3);
z-index: 1000;
text-align: center;
transition: transform 0.5s cubic-bezier(0.34, 1.56, 0.64, 1);
}
.achievement-popup.show {
transform: translate(-50%, -50%) scale(1);
}
.achievement-icon-large {
font-size: 80px;
margin-bottom: var(--space-4);
animation: achievementBounce 1s ease infinite;
}
@keyframes achievementBounce {
0%, 100% { transform: translateY(0); }
50% { transform: translateY(-10px); }
}
.achievement-title {
font-size: var(--text-2xl);
font-weight: 700;
color: var(--primary-teal);
margin-bottom: var(--space-2);
}
.achievement-description {
font-size: var(--text-base);
color: var(--text-secondary);
margin-bottom: var(--space-6);
}
/* 提示訊息 */
.hint-message {
background: linear-gradient(135deg, rgba(0, 229, 204, 0.1), rgba(0, 229, 204, 0.05));
border-left: 3px solid var(--primary-teal);
padding: var(--space-3) var(--space-4);
border-radius: var(--radius-lg);
margin-bottom: var(--space-4);
font-size: var(--text-sm);
color: var(--text-primary);
display: flex;
align-items: center;
gap: var(--space-3);
}
/* 連擊效果 */
.combo-indicator {
position: fixed;
top: 100px;
right: 20px;
background: linear-gradient(135deg, var(--warning-yellow), var(--gold));
color: var(--background-dark);
padding: var(--space-3) var(--space-4);
border-radius: var(--radius-xl);
font-weight: 700;
box-shadow: 0 4px 16px rgba(255, 215, 0, 0.3);
opacity: 0;
transform: translateX(100px);
transition: all 0.3s ease;
}
.combo-indicator.show {
opacity: 1;
transform: translateX(0);
}
.combo-number {
font-size: var(--text-2xl);
margin-right: var(--space-2);
}
/* 響應式設計 */
@media (max-width: 768px) {
.learning-container {
padding: var(--space-2);
}
.learning-card {
padding: var(--space-6);
min-height: 350px;
}
.word-main {
font-size: var(--text-3xl);
}
.options-container {
grid-template-columns: 1fr;
}
.action-buttons {
flex-direction: column;
}
.skip-button,
.continue-button {
width: 100%;
}
}
/* 粒子效果 */
.particle {
position: fixed;
pointer-events: none;
animation: particleFloat 3s ease-out forwards;
}
@keyframes particleFloat {
0% {
opacity: 1;
transform: translateY(0) scale(1);
}
100% {
opacity: 0;
transform: translateY(-100px) scale(0);
}
}
</style>
</head>
<body>
<div class="learning-container">
<!-- 頂部狀態欄 -->
<header class="learning-header">
<div class="header-left">
<a href="../index.html" class="back-button"></a>
<div class="level-info">
<span>📚</span>
<span>Level 3 - 第5課</span>
</div>
</div>
<div class="header-right">
<!-- 生命值 -->
<div class="life-bar">
<span class="life-heart">❤️</span>
<span class="life-heart">❤️</span>
<span class="life-heart">❤️</span>
<span class="life-heart">❤️</span>
<span class="life-heart empty">❤️</span>
</div>
<!-- 鑽石數量 -->
<div style="display: flex; align-items: center; gap: var(--space-2); background: var(--card-background); padding: var(--space-2) var(--space-3); border-radius: var(--radius-full); border: 1px solid var(--divider);">
<span>💎</span>
<span style="font-weight: 600; color: var(--primary-teal);">156</span>
</div>
</div>
</header>
<!-- 進度條 -->
<div class="progress-container">
<div class="learning-progress" style="width: 30%"></div>
</div>
<!-- 學習卡片 -->
<div class="learning-card">
<!-- 詞彙展示 -->
<div class="word-display">
<h1 class="word-main">Restaurant</h1>
<p class="word-pronunciation">[ˈrestərɑnt]</p>
<p class="word-translation">餐廳</p>
<!-- 語音播放按鈕 -->
<button class="audio-button" onclick="playAudio()">
🔊
</button>
<!-- 例句 -->
<div class="word-example">
<p class="example-sentence">
We're going to have dinner at a nice <strong>restaurant</strong> tonight.
</p>
<p class="example-translation">
我們今晚要去一家不錯的餐廳吃晚餐。
</p>
</div>
</div>
<!-- 提示訊息 -->
<div class="hint-message">
<span>💡</span>
<span>點擊喇叭按鈕聽發音,幫助你記憶單字!</span>
</div>
</div>
<!-- 選項區(練習模式) -->
<div class="options-container" style="display: none;">
<button class="option-button" onclick="checkAnswer(this, false)">Hotel</button>
<button class="option-button" onclick="checkAnswer(this, true)">Restaurant</button>
<button class="option-button" onclick="checkAnswer(this, false)">Market</button>
<button class="option-button" onclick="checkAnswer(this, false)">Station</button>
</div>
<!-- 底部操作 -->
<footer class="learning-footer">
<div class="action-buttons">
<button class="skip-button" onclick="skipWord()">跳過</button>
<button class="continue-button" onclick="nextWord()">繼續</button>
</div>
</footer>
</div>
<!-- 連擊指示器 -->
<div class="combo-indicator" id="comboIndicator">
<span class="combo-number">3</span>
<span>連擊!</span>
</div>
<!-- 成就彈窗 -->
<div class="achievement-popup" id="achievementPopup">
<div class="achievement-icon-large">🏆</div>
<h2 class="achievement-title">首次完成!</h2>
<p class="achievement-description">你完成了第一個詞彙學習獲得10經驗值</p>
<button class="btn btn-primary" onclick="closeAchievement()">太棒了!</button>
</div>
<script>
let currentProgress = 30;
let comboCount = 0;
let currentMode = 'learning'; // learning or practice
// 播放音訊
function playAudio() {
const button = event.target;
button.classList.add('playing');
// 模擬播放
setTimeout(() => {
button.classList.remove('playing');
}, 1000);
// 創建粒子效果
createParticles(button);
}
// 下一個詞彙
function nextWord() {
// 更新進度條
currentProgress = Math.min(currentProgress + 10, 100);
document.querySelector('.learning-progress').style.width = currentProgress + '%';
// 切換到練習模式
if (currentMode === 'learning') {
switchToPracticeMode();
} else {
// 卡片動畫
const card = document.querySelector('.learning-card');
card.style.animation = 'none';
setTimeout(() => {
card.style.animation = 'cardSlideIn 0.5s ease-out';
}, 10);
// 重置選項
document.querySelectorAll('.option-button').forEach(btn => {
btn.classList.remove('correct', 'incorrect');
btn.disabled = false;
});
// 檢查是否完成
if (currentProgress >= 100) {
showAchievement();
}
}
}
// 切換到練習模式
function switchToPracticeMode() {
currentMode = 'practice';
// 顯示提示
const hint = document.querySelector('.hint-message');
hint.innerHTML = '<span>📝</span><span>選擇正確的單字!</span>';
// 更新詞彙展示
const wordDisplay = document.querySelector('.word-display');
wordDisplay.innerHTML = `
<p class="word-translation" style="font-size: var(--text-2xl); margin-bottom: var(--space-6);">餐廳</p>
<p style="color: var(--text-secondary); font-size: var(--text-base);">請選擇對應的英文單字</p>
`;
// 顯示選項
document.querySelector('.options-container').style.display = 'grid';
// 禁用繼續按鈕
document.querySelector('.continue-button').disabled = true;
}
// 跳過詞彙
function skipWord() {
// 扣除生命值
const hearts = document.querySelectorAll('.life-heart:not(.empty)');
if (hearts.length > 0) {
hearts[hearts.length - 1].classList.add('empty');
hearts[hearts.length - 1].style.animation = 'heartPulse 0.5s ease';
}
// 重置連擊
comboCount = 0;
nextWord();
}
// 檢查答案
function checkAnswer(button, isCorrect) {
// 禁用所有選項
document.querySelectorAll('.option-button').forEach(btn => {
btn.disabled = true;
});
if (isCorrect) {
button.classList.add('correct');
// 增加連擊
comboCount++;
if (comboCount >= 3) {
showCombo();
}
// 啟用繼續按鈕
document.querySelector('.continue-button').disabled = false;
// 創建成功粒子
createSuccessParticles();
} else {
button.classList.add('incorrect');
// 扣除生命值
const hearts = document.querySelectorAll('.life-heart:not(.empty)');
if (hearts.length > 0) {
hearts[hearts.length - 1].classList.add('empty');
}
// 重置連擊
comboCount = 0;
// 顯示正確答案
setTimeout(() => {
document.querySelectorAll('.option-button').forEach(btn => {
if (btn.textContent === 'Restaurant') {
btn.classList.add('correct');
}
});
document.querySelector('.continue-button').disabled = false;
}, 500);
}
}
// 顯示連擊
function showCombo() {
const indicator = document.getElementById('comboIndicator');
indicator.querySelector('.combo-number').textContent = comboCount;
indicator.classList.add('show');
setTimeout(() => {
indicator.classList.remove('show');
}, 2000);
}
// 顯示成就
function showAchievement() {
const popup = document.getElementById('achievementPopup');
popup.classList.add('show');
// 創建慶祝粒子
for (let i = 0; i < 20; i++) {
setTimeout(() => createCelebrationParticles(), i * 100);
}
}
// 關閉成就彈窗
function closeAchievement() {
const popup = document.getElementById('achievementPopup');
popup.classList.remove('show');
}
// 創建粒子效果
function createParticles(element) {
const rect = element.getBoundingClientRect();
for (let i = 0; i < 5; i++) {
const particle = document.createElement('div');
particle.className = 'particle';
particle.style.left = rect.left + rect.width / 2 + 'px';
particle.style.top = rect.top + rect.height / 2 + 'px';
particle.innerHTML = '🎵';
particle.style.fontSize = '20px';
particle.style.transform = `rotate(${Math.random() * 360}deg)`;
document.body.appendChild(particle);
setTimeout(() => particle.remove(), 3000);
}
}
// 創建成功粒子
function createSuccessParticles() {
const centerX = window.innerWidth / 2;
const centerY = window.innerHeight / 2;
const emojis = ['✨', '⭐', '🌟', '💫'];
for (let i = 0; i < 10; i++) {
const particle = document.createElement('div');
particle.className = 'particle';
particle.style.left = centerX + (Math.random() - 0.5) * 200 + 'px';
particle.style.top = centerY + (Math.random() - 0.5) * 200 + 'px';
particle.innerHTML = emojis[Math.floor(Math.random() * emojis.length)];
particle.style.fontSize = Math.random() * 20 + 15 + 'px';
document.body.appendChild(particle);
setTimeout(() => particle.remove(), 3000);
}
}
// 創建慶祝粒子
function createCelebrationParticles() {
const particle = document.createElement('div');
particle.className = 'particle';
particle.style.left = Math.random() * window.innerWidth + 'px';
particle.style.top = window.innerHeight + 'px';
particle.innerHTML = ['🎉', '🎊', '🏆', '⭐'][Math.floor(Math.random() * 4)];
particle.style.fontSize = Math.random() * 30 + 20 + 'px';
particle.style.animation = 'particleFloat 4s ease-out forwards';
document.body.appendChild(particle);
setTimeout(() => particle.remove(), 4000);
}
// 鍵盤快捷鍵
document.addEventListener('keydown', (e) => {
if (e.key === 'Enter' || e.key === ' ') {
e.preventDefault();
const continueBtn = document.querySelector('.continue-button');
if (!continueBtn.disabled) {
nextWord();
}
} else if (e.key === 'Escape') {
skipWord();
} else if (e.key >= '1' && e.key <= '4' && currentMode === 'practice') {
const options = document.querySelectorAll('.option-button');
const index = parseInt(e.key) - 1;
if (options[index] && !options[index].disabled) {
options[index].click();
}
}
});
// 初始化動畫
setTimeout(() => {
document.querySelector('.learning-card').style.opacity = '1';
}, 100);
</script>
</body>
</html>