简介
游戏厅(Arcade)是 Lumin 博客的一个独立页面,集成了 9 款经典小游戏,通过标签页切换实现单页多游戏体验。所有游戏均支持最高分本地存储和暗色主题适配。
游戏列表
| # | 游戏 | 实现方式 | 操作 |
|---|---|---|---|
| 1 | 五子棋 | DOM + CSS Grid | 鼠标点击 |
| 2 | 扫雷 | DOM + CSS Grid | 鼠标点击/右键 |
| 3 | 贪吃蛇 | Canvas | 方向键 |
| 4 | 俄罗斯方块 | Canvas | 方向键 + 空格 |
| 5 | 2048 | DOM + CSS Grid | 方向键 |
| 6 | Flappy Bird | Canvas | 空格/点击 |
| 7 | 记忆翻牌 | DOM + CSS 3D | 鼠标点击 |
| 8 | 打砖块 | Canvas | 方向键/鼠标 |
| 9 | 井字棋 | DOM + CSS Grid | 鼠标点击 |
架构设计
单页多面板架构
所有 9 个游戏放在同一个 HTML 文件中,通过 CSS display: none/block 切换面板:
1┌──────────────────────────────────────────────┐
2│ [五子棋] [扫雷] [贪吃蛇] [俄罗斯方块] │
3│ [2048] [Flappy Bird] [记忆翻牌] │ ← 标签栏
4│ [打砖块] [井字棋] │
5├──────────────────────────────────────────────┤
6│ │
7│ 游戏面板 (display: block) │ ← 当前活跃游戏
8│ │
9├──────────────────────────────────────────────┤
10│ 侧边栏 (分数/状态/操作) │ ← 游戏信息面板
11└──────────────────────────────────────────────┘
标签切换机制
1function switchGame(gameName) {
2 // 隐藏所有面板
3 document.querySelectorAll('.arcade-game-panel').forEach(function(p) {
4 p.classList.remove('active');
5 });
6 // 显示目标面板
7 document.getElementById('panel-' + gameName).classList.add('active');
8 // 更新标签状态
9 document.querySelectorAll('.arcade-tab').forEach(function(t) {
10 t.classList.toggle('active', t.dataset.game === gameName);
11 });
12}
Canvas 游戏生命周期
Canvas 游戏(贪吃蛇、俄罗斯方块、Flappy Bird、打砖块)需要注意切换时的生命周期管理:
1// 切换离开时停止动画循环
2function leaveGame() {
3 if (animFrameId) {
4 cancelAnimationFrame(animFrameId);
5 animFrameId = null;
6 }
7}
8
9// 切换回来时重新初始化
10function enterGame() {
11 init();
12 // 仅当面板激活且游戏状态为 playing 时启动动画
13 if (playing) animFrameId = requestAnimationFrame(gameLoop);
14}
键盘事件隔离
多个游戏使用相同的键盘事件(方向键),需要根据当前活跃面板过滤:
1document.addEventListener('keydown', function(e) {
2 // 不同游戏各自检查自己的面板是否活跃
3 if (!document.getElementById('panel-snake').classList.contains('active')) return;
4 // 处理方向键...
5});
样式设计
标签栏
每个标签使用彩色圆点区分游戏:
1.arcade-tab .tab-dot.gomoku { background: #3b82f6; } /* 蓝色 - 五子棋 */
2.arcade-tab .tab-dot.minesweeper { background: #ef4444; } /* 红色 - 扫雷 */
3.arcade-tab .tab-dot.snake { background: #22c55e; } /* 绿色 - 贪吃蛇 */
4.arcade-tab .tab-dot.tetris { background: #8b5cf6; } /* 紫色 - 俄罗斯方块 */
5.arcade-tab .tab-dot.tzfe { background: #f97316; } /* 橙色 - 2048 */
6.arcade-tab .tab-dot.flappy { background: #14b8a6; } /* 青色 - Flappy Bird */
7.arcade-tab .tab-dot.memory { background: #ec4899; } /* 粉色 - 记忆翻牌 */
8.arcade-tab .tab-dot.breakout { background: #ef4444; } /* 红色 - 打砖块 */
9.arcade-tab .tab-dot.tictactoe{ background: #6366f1; } /* 靛色 - 井字棋 */
游戏面板布局
每个游戏面板采用左右分栏布局:
1┌──────────────────────┬──────────────────┐
2│ │ 状态栏 │
3│ 游戏主区域 │ 当前分数 │
4│ (Canvas / Grid) │ 最高分 │
5│ │ 操作按键说明 │
6│ │ [重新开始] │
7└──────────────────────┴──────────────────┘
数据持久化
所有游戏使用 localStorage 存储最高分:
| 游戏 | localStorage Key |
|---|---|
| 贪吃蛇 | arcade-snake-best |
| 俄罗斯方块 | arcade-tetris-best |
| 2048 | arcade-tzfe-best |
| Flappy Bird | arcade-flappy-best |
| 打砖块 | arcade-breakout-best |
扫雷、五子棋、井字棋使用运行时状态,不需要持久化。
暗色主题适配
所有游戏组件通过 CSS 变量和 [data-theme="dark"] 选择器适配暗色模式:
1[data-theme="dark"] .game-board {
2 background: #1e293b;
3 border-color: #334155;
4}
5[data-theme="dark"] .game-card {
6 background: rgba(30, 41, 59, 0.8);
7}
Canvas 游戏通过 JavaScript 检测主题并切换绘制颜色:
1function getThemeColors() {
2 var isDark = document.documentElement.dataset.theme === 'dark';
3 return {
4 bg: isDark ? '#0f172a' : '#f8fafc',
5 grid: isDark ? '#1e293b' : '#e2e8f0',
6 text: isDark ? '#e2e8f0' : '#1e293b'
7 };
8}
响应式布局
通过 CSS 媒体查询适配移动端:
1@media (max-width: 900px) {
2 .game-body {
3 flex-direction: column;
4 }
5 .game-panel {
6 width: 100%;
7 }
8}
文件结构
1layouts/
2└── arcade/
3 └── list.html # 游戏厅主模板(全部 CSS + HTML + JS)
4
5content/
6└── arcade/
7 └── _index.md # 页面元数据(type: arcade, aside: false)
8
9hugo.toml # 导航菜单配置
总结
游戏厅采用单文件模板架构,将 9 款游戏的 CSS、HTML、JavaScript 全部内联在一个模板文件中,利用 Hugo 的 define "main" 和 define "scripts" 块分离结构与逻辑。这种设计避免了大量小文件的维护成本,同时通过标签切换实现流畅的游戏切换体验。
留言评论
期待你的想法评论加载中