Download index.html from SZFSPC/apple: direct link, hf CLI and curl.
- Browser
- Download file 26.7 kB
-
https://huggingface.co/spaces/SZFSPC/apple/resolve/main/index.html
- Command line
-
hf download hf://spaces/SZFSPC/apple/index.html
-
curl -L -o index.html https://huggingface.co/spaces/SZFSPC/apple/resolve/main/index.html
26.7 kB
| <html lang="zh-CN"> | |
| <head> | |
| <meta charset="UTF-8"> | |
| <meta name="viewport" content="width=device-width, initial-scale=1.0"> | |
| <title>Take a Break - Snake Game</title> | |
| <style> | |
| * { | |
| margin: 0; | |
| padding: 0; | |
| box-sizing: border-box; | |
| } | |
| body { | |
| font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif; | |
| background: linear-gradient(135deg, #1a1a2e 0%, #16213e 50%, #0f3460 100%); | |
| min-height: 100vh; | |
| display: flex; | |
| flex-direction: column; | |
| justify-content: center; | |
| align-items: center; | |
| overflow: hidden; | |
| position: relative; | |
| } | |
| /* 背景动画粒子 */ | |
| .particles { | |
| position: fixed; | |
| top: 0; | |
| left: 0; | |
| width: 100%; | |
| height: 100%; | |
| pointer-events: none; | |
| overflow: hidden; | |
| z-index: 0; | |
| } | |
| .particle { | |
| position: absolute; | |
| width: 4px; | |
| height: 4px; | |
| background: rgba(255, 255, 255, 0.3); | |
| border-radius: 50%; | |
| animation: float 15s infinite; | |
| } | |
| @keyframes float { | |
| 0%, 100% { | |
| transform: translateY(100vh) rotate(0deg); | |
| opacity: 0; | |
| } | |
| 10% { | |
| opacity: 1; | |
| } | |
| 90% { | |
| opacity: 1; | |
| } | |
| 100% { | |
| transform: translateY(-100vh) rotate(720deg); | |
| opacity: 0; | |
| } | |
| } | |
| /* 主容器 */ | |
| .container { | |
| position: relative; | |
| z-index: 1; | |
| text-align: center; | |
| padding: 20px; | |
| } | |
| /* 标题区域 */ | |
| .header { | |
| margin-bottom: 20px; | |
| } | |
| .header h1 { | |
| color: #fff; | |
| font-size: 2.5em; | |
| margin-bottom: 10px; | |
| text-shadow: 0 0 20px rgba(0, 255, 255, 0.5); | |
| background: linear-gradient(90deg, #00ffff, #ff00ff, #ffff00); | |
| -webkit-background-clip: text; | |
| -webkit-text-fill-color: transparent; | |
| background-clip: text; | |
| animation: glow 2s ease-in-out infinite alternate; | |
| } | |
| @keyframes glow { | |
| from { | |
| filter: drop-shadow(0 0 10px rgba(0, 255, 255, 0.5)); | |
| } | |
| to { | |
| filter: drop-shadow(0 0 20px rgba(255, 0, 255, 0.5)); | |
| } | |
| } | |
| .header p { | |
| color: rgba(255, 255, 255, 0.7); | |
| font-size: 1.1em; | |
| } | |
| /* 游戏区域 */ | |
| .game-wrapper { | |
| display: inline-block; | |
| background: rgba(0, 0, 0, 0.6); | |
| border-radius: 20px; | |
| padding: 20px; | |
| box-shadow: | |
| 0 0 50px rgba(0, 255, 255, 0.2), | |
| inset 0 0 50px rgba(0, 0, 0, 0.5); | |
| border: 2px solid rgba(0, 255, 255, 0.3); | |
| } | |
| .score-board { | |
| display: flex; | |
| justify-content: space-around; | |
| margin-bottom: 15px; | |
| padding: 10px 15px; | |
| background: rgba(255, 255, 255, 0.1); | |
| border-radius: 10px; | |
| color: #fff; | |
| font-size: 1.1em; | |
| font-weight: bold; | |
| gap: 15px; | |
| } | |
| .score-item { | |
| display: flex; | |
| align-items: center; | |
| gap: 10px; | |
| } | |
| .score-label { | |
| color: rgba(255, 255, 255, 0.6); | |
| font-size: 0.8em; | |
| } | |
| .score-value { | |
| color: #00ffff; | |
| font-size: 1.3em; | |
| text-shadow: 0 0 10px rgba(0, 255, 255, 0.5); | |
| } | |
| #gameCanvas { | |
| border-radius: 10px; | |
| box-shadow: 0 0 30px rgba(0, 0, 0, 0.5); | |
| background: #0a0a0a; | |
| } | |
| /* 控制按钮 */ | |
| .controls { | |
| margin-top: 20px; | |
| display: flex; | |
| gap: 15px; | |
| justify-content: center; | |
| flex-wrap: wrap; | |
| } | |
| .btn { | |
| padding: 12px 30px; | |
| border: none; | |
| border-radius: 25px; | |
| font-size: 1em; | |
| font-weight: bold; | |
| cursor: pointer; | |
| transition: all 0.3s ease; | |
| text-transform: uppercase; | |
| letter-spacing: 1px; | |
| } | |
| .btn-primary { | |
| background: linear-gradient(135deg, #00ffff 0%, #0080ff 100%); | |
| color: #000; | |
| box-shadow: 0 4px 15px rgba(0, 255, 255, 0.4); | |
| } | |
| .btn-primary:hover { | |
| transform: translateY(-2px); | |
| box-shadow: 0 6px 25px rgba(0, 255, 255, 0.6); | |
| } | |
| .btn-secondary { | |
| background: rgba(255, 255, 255, 0.1); | |
| color: #fff; | |
| border: 2px solid rgba(255, 255, 255, 0.3); | |
| } | |
| .btn-secondary:hover { | |
| background: rgba(255, 255, 255, 0.2); | |
| border-color: rgba(255, 255, 255, 0.5); | |
| } | |
| /* 移动端控制 */ | |
| .mobile-controls { | |
| display: none; | |
| margin-top: 20px; | |
| } | |
| .d-pad { | |
| display: grid; | |
| grid-template-columns: repeat(3, 60px); | |
| grid-template-rows: repeat(3, 60px); | |
| gap: 5px; | |
| justify-content: center; | |
| } | |
| .d-btn { | |
| background: rgba(255, 255, 255, 0.1); | |
| border: 2px solid rgba(0, 255, 255, 0.3); | |
| border-radius: 10px; | |
| color: #fff; | |
| font-size: 1.5em; | |
| cursor: pointer; | |
| transition: all 0.2s; | |
| display: flex; | |
| align-items: center; | |
| justify-content: center; | |
| } | |
| .d-btn:hover, .d-btn:active { | |
| background: rgba(0, 255, 255, 0.3); | |
| border-color: #00ffff; | |
| } | |
| .d-btn:nth-child(2) { grid-column: 2; grid-row: 1; } | |
| .d-btn:nth-child(4) { grid-column: 1; grid-row: 2; } | |
| .d-btn:nth-child(5) { grid-column: 2; grid-row: 2; background: rgba(0, 255, 255, 0.2); } | |
| .d-btn:nth-child(6) { grid-column: 3; grid-row: 2; } | |
| .d-btn:nth-child(8) { grid-column: 2; grid-row: 3; } | |
| /* 提示信息 */ | |
| .tips { | |
| margin-top: 20px; | |
| color: rgba(255, 255, 255, 0.5); | |
| font-size: 0.9em; | |
| } | |
| .tips kbd { | |
| background: rgba(255, 255, 255, 0.2); | |
| padding: 2px 8px; | |
| border-radius: 4px; | |
| font-family: monospace; | |
| } | |
| /* 游戏结束覆盖层 */ | |
| .game-over { | |
| position: absolute; | |
| top: 50%; | |
| left: 50%; | |
| transform: translate(-50%, -50%); | |
| background: rgba(0, 0, 0, 0.9); | |
| padding: 40px; | |
| border-radius: 20px; | |
| text-align: center; | |
| display: none; | |
| border: 2px solid #ff0066; | |
| box-shadow: 0 0 50px rgba(255, 0, 102, 0.5); | |
| } | |
| .game-over h2 { | |
| color: #ff0066; | |
| font-size: 2em; | |
| margin-bottom: 20px; | |
| text-shadow: 0 0 20px rgba(255, 0, 102, 0.5); | |
| } | |
| .game-over .final-score { | |
| color: #fff; | |
| font-size: 1.5em; | |
| margin-bottom: 20px; | |
| } | |
| /* 响应式 */ | |
| @media (max-width: 600px) { | |
| .header h1 { | |
| font-size: 1.8em; | |
| } | |
| #gameCanvas { | |
| width: 100%; | |
| max-width: 350px; | |
| height: auto; | |
| } | |
| .mobile-controls { | |
| display: block; | |
| } | |
| .tips { | |
| display: none; | |
| } | |
| } | |
| /* 登录链接 */ | |
| .login-link { | |
| margin-top: 30px; | |
| color: rgba(255, 255, 255, 0.5); | |
| } | |
| .login-link a { | |
| color: #00ffff; | |
| text-decoration: none; | |
| border-bottom: 1px dashed #00ffff; | |
| transition: all 0.3s; | |
| } | |
| .login-link a:hover { | |
| color: #fff; | |
| border-bottom-style: solid; | |
| } | |
| </style> | |
| </head> | |
| <body> | |
| <!-- 背景粒子 --> | |
| <div class="particles" id="particles"></div> | |
| <div class="container"> | |
| <div class="header"> | |
| <h1>🐍 Snake Game</h1> | |
| <p>Take a break and enjoy a game!</p> | |
| </div> | |
| <div class="game-wrapper"> | |
| <div class="score-board"> | |
| <div class="score-item"> | |
| <span class="score-label">SCORE</span> | |
| <span class="score-value" id="score">0</span> | |
| </div> | |
| <div class="score-item" id="modeIndicator"> | |
| <span class="score-label" style="color: #ff00ff; text-shadow: 0 0 10px rgba(255, 0, 255, 0.5);">DEMO MODE</span> | |
| </div> | |
| <div class="score-item"> | |
| <span class="score-label">HIGH SCORE</span> | |
| <span class="score-value" id="highScore">0</span> | |
| </div> | |
| </div> | |
| <canvas id="gameCanvas" width="400" height="400"></canvas> | |
| <div class="game-over" id="gameOver"> | |
| <h2>GAME OVER</h2> | |
| <div class="final-score">Final Score: <span id="finalScore">0</span></div> | |
| <button class="btn btn-primary" onclick="startGame()">PLAY AGAIN</button> | |
| </div> | |
| <div class="controls"> | |
| <button class="btn btn-primary" id="startBtn" onclick="startGame()">CLICK TO PLAY</button> | |
| <button class="btn btn-secondary" onclick="pauseGame()" id="pauseBtn" disabled>PAUSE</button> | |
| </div> | |
| <!-- 移动端控制 --> | |
| <div class="mobile-controls"> | |
| <div class="d-pad"> | |
| <div class="d-btn" onclick="changeDirection('up')">▲</div> | |
| <div class="d-btn" onclick="changeDirection('left')">◀</div> | |
| <div class="d-btn">●</div> | |
| <div class="d-btn" onclick="changeDirection('right')">▶</div> | |
| <div class="d-btn" onclick="changeDirection('down')">▼</div> | |
| </div> | |
| </div> | |
| <div class="tips"> | |
| Use <kbd>↑</kbd> <kbd>↓</kbd> <kbd>←</kbd> <kbd>→</kbd> or <kbd>W</kbd> <kbd>S</kbd> <kbd>A</kbd> <kbd>D</kbd> to control | |
| </div> | |
| </div> | |
| <div class="login-link"> | |
| Need to manage your server? <a href="/login">Login here</a> | |
| </div> | |
| </div> | |
| <script> | |
| // 游戏配置 | |
| const canvas = document.getElementById('gameCanvas'); | |
| const ctx = canvas.getContext('2d'); | |
| const gridSize = 20; | |
| const tileCount = canvas.width / gridSize; | |
| let snake = []; | |
| let food = {}; | |
| let direction = 'right'; | |
| let nextDirection = 'right'; | |
| let score = 0; | |
| let highScore = localStorage.getItem('snakeHighScore') || 0; | |
| let gameLoop = null; | |
| let isPaused = false; | |
| let isGameOver = false; | |
| let gameSpeed = 100; | |
| let isDemoMode = true; // 演示模式标志 | |
| let isUserMode = false; // 用户交互模式标志 | |
| // 初始化最高分显示 | |
| document.getElementById('highScore').textContent = highScore; | |
| // 创建背景粒子 | |
| function createParticles() { | |
| const container = document.getElementById('particles'); | |
| for (let i = 0; i < 50; i++) { | |
| const particle = document.createElement('div'); | |
| particle.className = 'particle'; | |
| particle.style.left = Math.random() * 100 + '%'; | |
| particle.style.animationDelay = Math.random() * 15 + 's'; | |
| particle.style.animationDuration = (10 + Math.random() * 10) + 's'; | |
| container.appendChild(particle); | |
| } | |
| } | |
| // 初始化游戏 | |
| function initGame() { | |
| snake = [ | |
| { x: 5, y: 10 }, | |
| { x: 4, y: 10 }, | |
| { x: 3, y: 10 } | |
| ]; | |
| direction = 'right'; | |
| nextDirection = 'right'; | |
| score = 0; | |
| isGameOver = false; | |
| isPaused = false; | |
| gameSpeed = 100; | |
| document.getElementById('score').textContent = score; | |
| document.getElementById('gameOver').style.display = 'none'; | |
| if (isUserMode) { | |
| document.getElementById('startBtn').textContent = 'RESTART'; | |
| } else { | |
| document.getElementById('startBtn').textContent = 'CLICK TO PLAY'; | |
| } | |
| spawnFood(); | |
| } | |
| // AI自动寻路(演示模式) | |
| function getAIDirection() { | |
| const head = snake[0]; | |
| let bestDirection = direction; | |
| let minDistance = Infinity; | |
| const directions = ['up', 'down', 'left', 'right']; | |
| const opposites = { 'up': 'down', 'down': 'up', 'left': 'right', 'right': 'left' }; | |
| for (const dir of directions) { | |
| // 避免180度转向 | |
| if (opposites[dir] === direction) continue; | |
| let nextX = head.x; | |
| let nextY = head.y; | |
| switch(dir) { | |
| case 'up': nextY--; break; | |
| case 'down': nextY++; break; | |
| case 'left': nextX--; break; | |
| case 'right': nextX++; break; | |
| } | |
| // 检查是否会撞墙或撞到自己 | |
| if (nextX < 0 || nextX >= tileCount || nextY < 0 || nextY >= tileCount) continue; | |
| if (isSnakeAt(nextX, nextY)) continue; | |
| // 计算到食物的距离 | |
| const distance = Math.abs(nextX - food.x) + Math.abs(nextY - food.y); | |
| if (distance < minDistance) { | |
| minDistance = distance; | |
| bestDirection = dir; | |
| } | |
| } | |
| return bestDirection; | |
| } | |
| // 生成食物 | |
| function spawnFood() { | |
| do { | |
| food = { | |
| x: Math.floor(Math.random() * tileCount), | |
| y: Math.floor(Math.random() * tileCount) | |
| }; | |
| } while (isSnakeAt(food.x, food.y)); | |
| } | |
| // 检查蛇是否在指定位置 | |
| function isSnakeAt(x, y) { | |
| return snake.some(segment => segment.x === x && segment.y === y); | |
| } | |
| // 绘制游戏 | |
| function draw() { | |
| // 清空画布 | |
| ctx.fillStyle = '#0a0a0a'; | |
| ctx.fillRect(0, 0, canvas.width, canvas.height); | |
| // 绘制网格 | |
| ctx.strokeStyle = 'rgba(0, 255, 255, 0.05)'; | |
| ctx.lineWidth = 1; | |
| for (let i = 0; i <= tileCount; i++) { | |
| ctx.beginPath(); | |
| ctx.moveTo(i * gridSize, 0); | |
| ctx.lineTo(i * gridSize, canvas.height); | |
| ctx.stroke(); | |
| ctx.beginPath(); | |
| ctx.moveTo(0, i * gridSize); | |
| ctx.lineTo(canvas.width, i * gridSize); | |
| ctx.stroke(); | |
| } | |
| // 绘制食物(发光效果) | |
| const foodX = food.x * gridSize; | |
| const foodY = food.y * gridSize; | |
| // 食物光晕 | |
| const gradient = ctx.createRadialGradient( | |
| foodX + gridSize/2, foodY + gridSize/2, 0, | |
| foodX + gridSize/2, foodY + gridSize/2, gridSize | |
| ); | |
| gradient.addColorStop(0, 'rgba(255, 0, 102, 0.8)'); | |
| gradient.addColorStop(1, 'rgba(255, 0, 102, 0)'); | |
| ctx.fillStyle = gradient; | |
| ctx.fillRect(foodX - gridSize/2, foodY - gridSize/2, gridSize * 2, gridSize * 2); | |
| // 食物本体 | |
| ctx.fillStyle = '#ff0066'; | |
| ctx.shadowColor = '#ff0066'; | |
| ctx.shadowBlur = 20; | |
| ctx.fillRect(foodX + 2, foodY + 2, gridSize - 4, gridSize - 4); | |
| ctx.shadowBlur = 0; | |
| // 绘制蛇 | |
| snake.forEach((segment, index) => { | |
| const x = segment.x * gridSize; | |
| const y = segment.y * gridSize; | |
| if (index === 0) { | |
| // 蛇头 | |
| ctx.fillStyle = '#00ffff'; | |
| ctx.shadowColor = '#00ffff'; | |
| ctx.shadowBlur = 15; | |
| } else { | |
| // 蛇身渐变 | |
| const ratio = index / snake.length; | |
| const r = Math.floor(0 * (1 - ratio) + 0 * ratio); | |
| const g = Math.floor(255 * (1 - ratio) + 100 * ratio); | |
| const b = Math.floor(255 * (1 - ratio) + 200 * ratio); | |
| ctx.fillStyle = `rgb(${r}, ${g}, ${b})`; | |
| ctx.shadowColor = ctx.fillStyle; | |
| ctx.shadowBlur = 10; | |
| } | |
| ctx.fillRect(x + 1, y + 1, gridSize - 2, gridSize - 2); | |
| // 蛇眼 | |
| if (index === 0) { | |
| ctx.fillStyle = '#000'; | |
| ctx.shadowBlur = 0; | |
| const eyeSize = 3; | |
| let eye1X, eye1Y, eye2X, eye2Y; | |
| switch(direction) { | |
| case 'up': | |
| eye1X = x + 5; eye1Y = y + 5; | |
| eye2X = x + 14; eye2Y = y + 5; | |
| break; | |
| case 'down': | |
| eye1X = x + 5; eye1Y = y + 12; | |
| eye2X = x + 14; eye2Y = y + 12; | |
| break; | |
| case 'left': | |
| eye1X = x + 5; eye1Y = y + 5; | |
| eye2X = x + 5; eye2Y = y + 12; | |
| break; | |
| case 'right': | |
| eye1X = x + 12; eye1Y = y + 5; | |
| eye2X = x + 12; eye2Y = y + 12; | |
| break; | |
| } | |
| ctx.fillRect(eye1X, eye1Y, eyeSize, eyeSize); | |
| ctx.fillRect(eye2X, eye2Y, eyeSize, eyeSize); | |
| } | |
| }); | |
| ctx.shadowBlur = 0; | |
| } | |
| // 更新游戏状态 | |
| function update() { | |
| if (isPaused || isGameOver) return; | |
| // 演示模式下AI自动控制 | |
| if (isDemoMode && !isUserMode) { | |
| nextDirection = getAIDirection(); | |
| } | |
| direction = nextDirection; | |
| const head = { ...snake[0] }; | |
| switch(direction) { | |
| case 'up': head.y--; break; | |
| case 'down': head.y++; break; | |
| case 'left': head.x--; break; | |
| case 'right': head.x++; break; | |
| } | |
| // 检查碰撞 | |
| if (head.x < 0 || head.x >= tileCount || head.y < 0 || head.y >= tileCount) { | |
| if (isDemoMode && !isUserMode) { | |
| // 演示模式下自动重新开始 | |
| initGame(); | |
| return; | |
| } | |
| gameOver(); | |
| return; | |
| } | |
| if (isSnakeAt(head.x, head.y)) { | |
| if (isDemoMode && !isUserMode) { | |
| // 演示模式下自动重新开始 | |
| initGame(); | |
| return; | |
| } | |
| gameOver(); | |
| return; | |
| } | |
| snake.unshift(head); | |
| // 检查是否吃到食物 | |
| if (head.x === food.x && head.y === food.y) { | |
| score += 10; | |
| document.getElementById('score').textContent = score; | |
| // 更新最高分 | |
| if (score > highScore) { | |
| highScore = score; | |
| localStorage.setItem('snakeHighScore', highScore); | |
| document.getElementById('highScore').textContent = highScore; | |
| } | |
| // 加速 | |
| if (gameSpeed > 50) { | |
| gameSpeed -= 2; | |
| clearInterval(gameLoop); | |
| gameLoop = setInterval(gameStep, gameSpeed); | |
| } | |
| spawnFood(); | |
| } else { | |
| snake.pop(); | |
| } | |
| } | |
| // 游戏步进 | |
| function gameStep() { | |
| update(); | |
| draw(); | |
| } | |
| // 游戏结束 | |
| function gameOver() { | |
| isGameOver = true; | |
| clearInterval(gameLoop); | |
| document.getElementById('finalScore').textContent = score; | |
| document.getElementById('gameOver').style.display = 'block'; | |
| } | |
| // 切换到用户模式 | |
| function switchToUserMode() { | |
| if (!isUserMode) { | |
| isUserMode = true; | |
| isDemoMode = false; | |
| document.getElementById('startBtn').textContent = 'RESTART'; | |
| document.getElementById('pauseBtn').disabled = false; | |
| // 更新模式指示器 | |
| const modeIndicator = document.getElementById('modeIndicator'); | |
| if (modeIndicator) { | |
| modeIndicator.innerHTML = '<span class="score-label" style="color: #00ff00; text-shadow: 0 0 10px rgba(0, 255, 0, 0.5);">USER MODE</span>'; | |
| } | |
| // 显示提示 | |
| showModeIndicator('USER MODE'); | |
| } | |
| } | |
| // 显示模式指示器 | |
| function showModeIndicator(text) { | |
| const indicator = document.createElement('div'); | |
| indicator.style.cssText = ` | |
| position: absolute; | |
| top: 50%; | |
| left: 50%; | |
| transform: translate(-50%, -50%); | |
| background: rgba(0, 255, 255, 0.9); | |
| color: #000; | |
| padding: 15px 30px; | |
| border-radius: 10px; | |
| font-weight: bold; | |
| font-size: 1.2em; | |
| z-index: 100; | |
| animation: fadeInOut 1.5s ease-in-out; | |
| `; | |
| indicator.textContent = text; | |
| document.querySelector('.game-wrapper').appendChild(indicator); | |
| setTimeout(() => indicator.remove(), 1500); | |
| } | |
| // 添加淡入淡出动画 | |
| const style = document.createElement('style'); | |
| style.textContent = ` | |
| @keyframes fadeInOut { | |
| 0% { opacity: 0; transform: translate(-50%, -50%) scale(0.8); } | |
| 50% { opacity: 1; transform: translate(-50%, -50%) scale(1); } | |
| 100% { opacity: 0; transform: translate(-50%, -50%) scale(0.8); } | |
| } | |
| `; | |
| document.head.appendChild(style); | |
| // 开始游戏 | |
| function startGame() { | |
| clearInterval(gameLoop); | |
| switchToUserMode(); | |
| initGame(); | |
| draw(); | |
| gameLoop = setInterval(gameStep, gameSpeed); | |
| } | |
| // 暂停游戏 | |
| function pauseGame() { | |
| if (isGameOver) return; | |
| isPaused = !isPaused; | |
| event.target.textContent = isPaused ? 'RESUME' : 'PAUSE'; | |
| } | |
| // 改变方向 | |
| function changeDirection(newDirection) { | |
| // 第一次用户操作时切换到用户模式 | |
| switchToUserMode(); | |
| const opposites = { | |
| 'up': 'down', | |
| 'down': 'up', | |
| 'left': 'right', | |
| 'right': 'left' | |
| }; | |
| if (opposites[newDirection] !== direction) { | |
| nextDirection = newDirection; | |
| } | |
| } | |
| // 键盘控制 | |
| document.addEventListener('keydown', (e) => { | |
| switch(e.key) { | |
| case 'ArrowUp': | |
| case 'w': | |
| case 'W': | |
| e.preventDefault(); | |
| changeDirection('up'); | |
| break; | |
| case 'ArrowDown': | |
| case 's': | |
| case 'S': | |
| e.preventDefault(); | |
| changeDirection('down'); | |
| break; | |
| case 'ArrowLeft': | |
| case 'a': | |
| case 'A': | |
| e.preventDefault(); | |
| changeDirection('left'); | |
| break; | |
| case 'ArrowRight': | |
| case 'd': | |
| case 'D': | |
| e.preventDefault(); | |
| changeDirection('right'); | |
| break; | |
| case ' ': | |
| e.preventDefault(); | |
| pauseGame(); | |
| break; | |
| } | |
| }); | |
| // 触摸控制(防止页面滚动) | |
| document.addEventListener('touchmove', (e) => { | |
| e.preventDefault(); | |
| }, { passive: false }); | |
| // 点击画布切换到用户模式 | |
| canvas.addEventListener('click', () => { | |
| switchToUserMode(); | |
| }); | |
| // 点击移动端控制按钮切换到用户模式 | |
| document.querySelectorAll('.d-btn').forEach(btn => { | |
| btn.addEventListener('click', () => { | |
| switchToUserMode(); | |
| }); | |
| }); | |
| // 初始化 | |
| createParticles(); | |
| initGame(); | |
| draw(); | |
| gameLoop = setInterval(gameStep, gameSpeed); | |
| </script> | |
| </body> | |
| </html> | |