|
|
@@ -0,0 +1,243 @@
|
|
|
+
|
|
|
+<ion-content class="word-story-content" [class.has-fixed-bottom-bar]="!gameState.isGameOver">
|
|
|
+ <div class="ocean-bg"></div>
|
|
|
+
|
|
|
+ <!-- 漂浮的海洋生物装饰 -->
|
|
|
+ <div class="floating-creatures">
|
|
|
+ <div class="creature fish-1">
|
|
|
+ <img src="assets/icon/鲨鱼.svg" alt="fish" class="fish-svg">
|
|
|
+ </div>
|
|
|
+ <div class="creature fish-2">
|
|
|
+ <img src="assets/icon/鲨鱼.svg" alt="fish" class="fish-svg">
|
|
|
+ </div>
|
|
|
+ <div class="creature jellyfish">
|
|
|
+ <svg viewBox="0 0 100 100" width="40" height="40">
|
|
|
+ <circle cx="50" cy="30" r="15" fill="#FF80AB" opacity="0.6"/>
|
|
|
+ <path d="M35,40 Q50,70 65,40" stroke="#FF80AB" stroke-width="8" fill="none" opacity="0.5"/>
|
|
|
+ <path d="M30,45 Q45,80 60,45" stroke="#FF80AB" stroke-width="6" fill="none" opacity="0.5"/>
|
|
|
+ <path d="M40,45 Q50,85 70,45" stroke="#FF80AB" stroke-width="7" fill="none" opacity="0.5"/>
|
|
|
+ </svg>
|
|
|
+ </div>
|
|
|
+ <div class="creature bubble-1"></div>
|
|
|
+ <div class="creature bubble-2"></div>
|
|
|
+ <div class="creature bubble-3"></div>
|
|
|
+ <div class="creature starfish">
|
|
|
+ <svg viewBox="0 0 100 100" width="30" height="30">
|
|
|
+ <path d="M50,10 L60,35 L85,35 L65,50 L75,80 L50,65 L25,80 L35,50 L15,35 L40,35 Z" fill="#FFB74D" opacity="0.7"/>
|
|
|
+ </svg>
|
|
|
+ </div>
|
|
|
+ </div>
|
|
|
+
|
|
|
+ <div class="word-story-container">
|
|
|
+ <!-- 顶部导航 - 完全套用串词成文页面的船图标 -->
|
|
|
+ <div class="nav-header">
|
|
|
+ <div class="back-btn" (click)="goBack()">
|
|
|
+ <ion-icon name="arrow-back-outline"></ion-icon>
|
|
|
+ </div>
|
|
|
+ <h1 class="page-title">
|
|
|
+ <ion-icon name="boat-outline" class="title-icon"></ion-icon>
|
|
|
+ 单词配对消消乐
|
|
|
+ </h1>
|
|
|
+ <div class="placeholder"></div>
|
|
|
+ </div>
|
|
|
+
|
|
|
+ <!-- 游戏进行中 -->
|
|
|
+ <ng-container *ngIf="!gameState.isGameOver">
|
|
|
+ <!-- 难度选择器包装容器 - 垂直居中 -->
|
|
|
+ <div class="difficulty-selector-wrapper" *ngIf="gameState.cards.length === 0">
|
|
|
+ <!-- 难度选择器 - 海洋风格波浪装饰 -->
|
|
|
+ <div class="difficulty-selector">
|
|
|
+ <div class="wave-decoration">
|
|
|
+ <svg viewBox="0 0 1200 120" preserveAspectRatio="none">
|
|
|
+ <path d="M0,0V46.29c47.79,22.2,103.59,32.17,158,28,70.36-5.37,136.33-33.31,206.8-37.5C438.64,32.43,512.34,53.67,583,72.05c69.27,18,138.3,24.88,209.4,13.08,36.15-6,69.85-17.84,104.45-29.34C989.49,25,1113-14.29,1200,52.47V0Z" opacity=".25" fill="#81D4FA"></path>
|
|
|
+ <path d="M0,0V15.81C13,21.25,27.93,25.67,44.24,28.45c69.76,11.65,139.79-5,208.74-16.68C364.06,0,440.45,-6.64,521.46,6.68c55.82,9.13,108.61,27.27,162.23,40.29C741.57,62.54,812.19,72,884.66,66.82c42.43-3.13,84.16-12.18,125.26-23.88C1064.12,29.27,1137.54,7.47,1200,27.38V0Z" fill="#E1F5FE"></path>
|
|
|
+ </svg>
|
|
|
+ </div>
|
|
|
+
|
|
|
+ <!-- 添加锚点装饰 -->
|
|
|
+ <div class="anchor-decoration">
|
|
|
+ <svg width="40" height="40" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
|
|
|
+ <path d="M12 2C10.9 2 10 2.9 10 4V4.29C7.12 5.14 5 7.82 5 11V13L3 15V17H21V15L19 13V11C19 7.82 16.88 5.14 14 4.29V4C14 2.9 13.1 2 12 2ZM12 6C14.21 6 16 7.79 16 10V13H8V10C8 7.79 9.79 6 12 6ZM7 19V20C7 21.1 7.9 22 9 22H15C16.1 22 17 21.1 17 20V19H7Z" fill="#0288D1"/>
|
|
|
+ </svg>
|
|
|
+ </div>
|
|
|
+
|
|
|
+ <p class="difficulty-title">选择难度开始游戏</p>
|
|
|
+
|
|
|
+ <div class="difficulty-buttons-row">
|
|
|
+ <div
|
|
|
+ class="difficulty-option"
|
|
|
+ [class.selected]="selectedDifficulty === 'easy'"
|
|
|
+ (click)="selectedDifficulty = 'easy'; $event.stopPropagation()"
|
|
|
+ >
|
|
|
+ <span class="option-icon">🐟</span>
|
|
|
+ <span class="option-text">简单</span>
|
|
|
+ </div>
|
|
|
+ <div
|
|
|
+ class="difficulty-option"
|
|
|
+ [class.selected]="selectedDifficulty === 'medium'"
|
|
|
+ (click)="selectedDifficulty = 'medium'; $event.stopPropagation()"
|
|
|
+ >
|
|
|
+ <span class="option-icon">🐠</span>
|
|
|
+ <span class="option-text">中等</span>
|
|
|
+ </div>
|
|
|
+ <div
|
|
|
+ class="difficulty-option"
|
|
|
+ [class.selected]="selectedDifficulty === 'hard'"
|
|
|
+ (click)="selectedDifficulty = 'hard'; $event.stopPropagation()"
|
|
|
+ >
|
|
|
+ <span class="option-icon">🐡</span>
|
|
|
+ <span class="option-text">困难</span>
|
|
|
+ </div>
|
|
|
+ </div>
|
|
|
+
|
|
|
+ <ion-button
|
|
|
+ (click)="selectedDifficulty && startGame(selectedDifficulty)"
|
|
|
+ class="select-btn"
|
|
|
+ [class.active]="selectedDifficulty !== null"
|
|
|
+ [disabled]="selectedDifficulty === null"
|
|
|
+ expand="block"
|
|
|
+ >
|
|
|
+ <ion-icon name="play-outline" slot="start"></ion-icon>
|
|
|
+ 开始游戏
|
|
|
+ </ion-button>
|
|
|
+
|
|
|
+ <!-- 底部海星装饰 -->
|
|
|
+ <div class="starfish-decoration">
|
|
|
+ <svg viewBox="0 0 100 100" width="30" height="30">
|
|
|
+ <path d="M50,10 L60,35 L85,35 L65,50 L75,80 L50,65 L25,80 L35,50 L15,35 L40,35 Z" fill="#FFB74D"/>
|
|
|
+ </svg>
|
|
|
+ </div>
|
|
|
+ </div>
|
|
|
+ </div>
|
|
|
+
|
|
|
+ <!-- 游戏信息栏 - 仅在游戏开始后显示 -->
|
|
|
+ <div class="game-header shell-card" *ngIf="gameState.cards.length > 0">
|
|
|
+ <div class="stat-item">
|
|
|
+ <ion-icon name="time-outline" class="stat-icon"></ion-icon>
|
|
|
+ <span class="stat-label">时间</span>
|
|
|
+ <span class="stat-value">{{gameState.timeLeft}}s</span>
|
|
|
+ </div>
|
|
|
+ <div class="stat-divider"></div>
|
|
|
+ <div class="stat-item">
|
|
|
+ <ion-icon name="star-outline" class="stat-icon"></ion-icon>
|
|
|
+ <span class="stat-label">分数</span>
|
|
|
+ <span class="stat-value">{{gameState.score}}</span>
|
|
|
+ </div>
|
|
|
+ <div class="stat-divider"></div>
|
|
|
+ <div class="stat-item">
|
|
|
+ <ion-icon name="checkmark-circle-outline" class="stat-icon"></ion-icon>
|
|
|
+ <span class="stat-label">匹配</span>
|
|
|
+ <span class="stat-value">{{gameState.matchedPairs}}/{{getTotalPairs()}}</span>
|
|
|
+ </div>
|
|
|
+ </div>
|
|
|
+
|
|
|
+ <!-- 卡片网格 - 添加海浪效果 -->
|
|
|
+ <div class="cards-grid-container" *ngIf="gameState.cards.length > 0">
|
|
|
+ <div class="wave-overlay"></div>
|
|
|
+ <div class="cards-grid" [style.--grid-cols]="gameState.cards.length <= 8 ? 3 : 4">
|
|
|
+ <div
|
|
|
+ class="card-wrapper"
|
|
|
+ *ngFor="let card of gameState.cards; let i = index"
|
|
|
+ >
|
|
|
+ <div
|
|
|
+ class="card"
|
|
|
+ [class.flipped]="card.isFlipped"
|
|
|
+ [class.matched]="card.isMatched"
|
|
|
+ (click)="onCardClick(i)"
|
|
|
+ >
|
|
|
+ <div class="card-inner">
|
|
|
+ <!-- 卡片正面:鲨鱼图片 -->
|
|
|
+ <div class="card-front">
|
|
|
+ <div class="shark-container">
|
|
|
+ <img src="assets/icon/鲨鱼.svg" alt="shark" class="shark-image">
|
|
|
+ </div>
|
|
|
+ </div>
|
|
|
+ <!-- 卡片背面:单词/释义 -->
|
|
|
+ <div class="card-back">
|
|
|
+ <span class="card-text">{{card.content}}</span>
|
|
|
+ </div>
|
|
|
+ </div>
|
|
|
+ </div>
|
|
|
+ </div>
|
|
|
+ </div>
|
|
|
+ </div>
|
|
|
+
|
|
|
+ <!-- 底部提示信息 -->
|
|
|
+ <div class="game-tip" *ngIf="gameState.cards.length > 0">
|
|
|
+ <ion-icon name="information-circle-outline" class="tip-icon"></ion-icon>
|
|
|
+ <span class="tip-text">点击卡片配对英文和中文</span>
|
|
|
+ </div>
|
|
|
+ </ng-container>
|
|
|
+
|
|
|
+ <!-- 游戏结束 -->
|
|
|
+ <div class="game-over" *ngIf="gameState.isGameOver">
|
|
|
+ <div class="result-card shell-card">
|
|
|
+ <!-- 添加胜利/失败动画 -->
|
|
|
+ <div class="confetti" *ngIf="gameState.matchedPairs === getTotalPairs()">
|
|
|
+ <div class="confetti-piece"></div>
|
|
|
+ <div class="confetti-piece"></div>
|
|
|
+ <div class="confetti-piece"></div>
|
|
|
+ <div class="confetti-piece"></div>
|
|
|
+ </div>
|
|
|
+
|
|
|
+ <div class="result-icon" [class.win]="gameState.matchedPairs === getTotalPairs()">
|
|
|
+ <div class="bubble-animation"></div>
|
|
|
+ <img *ngIf="gameState.matchedPairs === getTotalPairs()" src="assets/icon/鲨鱼.svg" alt="win" class="result-shark">
|
|
|
+ <span *ngIf="gameState.matchedPairs !== getTotalPairs()">⏰</span>
|
|
|
+ </div>
|
|
|
+
|
|
|
+ <h2 class="result-title ocean-title">
|
|
|
+ {{gameState.matchedPairs === getTotalPairs() ? '恭喜获胜!' : '游戏结束!'}}
|
|
|
+ </h2>
|
|
|
+
|
|
|
+ <div class="treasure-chest" *ngIf="gameState.matchedPairs === getTotalPairs()">
|
|
|
+ <svg width="60" height="60" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
|
|
|
+ <rect x="4" y="8" width="16" height="12" rx="2" fill="#FFB74D"/>
|
|
|
+ <rect x="6" y="6" width="12" height="4" fill="#FF9800"/>
|
|
|
+ <circle cx="12" cy="14" r="2" fill="#FFD54F"/>
|
|
|
+ <circle cx="12" cy="14" r="1" fill="#FFF9C4"/>
|
|
|
+ <path d="M9 19L8 22H16L15 19H9Z" fill="#FFB74D"/>
|
|
|
+ </svg>
|
|
|
+ <span class="treasure-text">获得宝藏!</span>
|
|
|
+ </div>
|
|
|
+
|
|
|
+ <div class="result-stats">
|
|
|
+ <div class="stat-item">
|
|
|
+ <ion-icon name="trophy-outline" class="stat-icon"></ion-icon>
|
|
|
+ <span class="stat-label">最终分数</span>
|
|
|
+ <span class="stat-value ocean-number">{{gameState.score}}</span>
|
|
|
+ </div>
|
|
|
+ <div class="stat-item">
|
|
|
+ <ion-icon name="fish-outline" class="stat-icon"></ion-icon>
|
|
|
+ <span class="stat-label">匹配对数</span>
|
|
|
+ <span class="stat-value ocean-number">{{gameState.matchedPairs}}/{{getTotalPairs()}}</span>
|
|
|
+ </div>
|
|
|
+ <div class="stat-item">
|
|
|
+ <ion-icon name="hourglass-outline" class="stat-icon"></ion-icon>
|
|
|
+ <span class="stat-label">用时</span>
|
|
|
+ <span class="stat-value ocean-number">{{90 - gameState.timeLeft}}s</span>
|
|
|
+ </div>
|
|
|
+ </div>
|
|
|
+
|
|
|
+ <div class="button-group">
|
|
|
+ <ion-button
|
|
|
+ (click)="startGame(gameState.difficulty)"
|
|
|
+ class="ocean-btn primary"
|
|
|
+ expand="block"
|
|
|
+ >
|
|
|
+ <ion-icon name="refresh-outline" slot="start"></ion-icon>
|
|
|
+ 再玩一次
|
|
|
+ </ion-button>
|
|
|
+ <ion-button
|
|
|
+ (click)="resetToDifficultySelect()"
|
|
|
+ class="ocean-btn secondary"
|
|
|
+ expand="block"
|
|
|
+ >
|
|
|
+ <ion-icon name="options-outline" slot="start"></ion-icon>
|
|
|
+ 选择难度
|
|
|
+ </ion-button>
|
|
|
+ </div>
|
|
|
+ </div>
|
|
|
+ </div>
|
|
|
+ </div>
|
|
|
+</ion-content>
|