PageSourceSearch

https://fangg.netlify.app/js/apps/snake.js

js fangg.netlify.app collected 2026-10-03 10:43:27 UTC 5,974 bytes, 233 lines download raw bytes

1// 贪吃蛇游戏状态
2let snake = [];
3let food = { x: 0, y: 0 };
4let direction = 'right';
5let gameInterval = null;
6let score = 0;
7let isPaused = false;
8let gameStarted = false;
9let highScore = 0;
10
11// 游戏配置
12const config = {
13    gridSize: 20,
14    speed: 150,
15    initialLength: 3
16};
17
18// 初始化游戏
19function initGame() {
20    const canvas = document.getElementById('snake-canvas');
21    const ctx = canvas.getContext('2d');
22    
23    // 初始化蛇的位置
24    snake = [];
25    for (let i = 0; i < config.initialLength; i++) {
26        snake.push({
27            x: Math.floor(config.gridSize / 2) - i,
28            y: Math.floor(config.gridSize / 2)
29        });
30    }
31    
32    // 生成第一个食物
33    generateFood();
34    
35    // 重置分数
36    score = 0;
37    updateScore();
38    
39    // 清除画布
40    ctx.clearRect(0, 0, canvas.width, canvas.height);
41    
42    // 绘制初始状态
43    drawGame();
44}
45
46// 生成食物
47function generateFood() {
48    let newFood;
49    do {
50        newFood = {
51            x: Math.floor(Math.random() * config.gridSize),
52            y: Math.floor(Math.random() * config.gridSize)
53        };
54    } while (snake.some(segment => segment.x === newFood.x && segment.y === newFood.y));
55    
56    food = newFood;
57}
58
59// 更新游戏状态
60function updateGame() {
61    if (isPaused || !gameStarted) return;
62    
63    // 获取蛇头的下一个位置
64    const head = { ...snake[0] };
65    switch (direction) {
66        case 'up':
67            head.y--;
68            break;
69        case 'down':
70            head.y++;
71            break;
72        case 'left':
73            head.x--;
74            break;
75        case 'right':
76            head.x++;
77            break;
78    }
79    
80    // 检查是否撞墙
81    if (head.x < 0 || head.x >= config.gridSize || head.y < 0 || head.y >= config.gridSize) {
82        gameOver();
83        return;
84    }
85    
86    // 检查是否撞到自己
87    if (snake.some(segment => segment.x === head.x && segment.y === head.y)) {
88        gameOver();
89        return;
90    }
91    
92    // 移动蛇
93    snake.unshift(head);
94    
95    // 检查是否吃到食物
96    if (head.x === food.x && head.y === food.y) {
97        score += 10;
98        updateScore();
99        generateFood();
100    } else {
101        snake.pop();
102    }
103    
104    // 重新绘制游戏
105    drawGame();
106}
107
108// 绘制游戏
109function drawGame() {
110    const canvas = document.getElementById('snake-canvas');
111    const ctx = canvas.getContext('2d');
112    const cellSize = canvas.width / config.gridSize;
113    
114    // 清除画布
115    ctx.clearRect(0, 0, canvas.width, canvas.height);
116    
117    // 绘制网格
118    ctx.strokeStyle = '#ccc';
119    for (let i = 0; i <= config.gridSize; i++) {
120        ctx.beginPath();
121        ctx.moveTo(i * cellSize, 0);
122        ctx.lineTo(i * cellSize, canvas.height);
123        ctx.stroke();
124        
125        ctx.beginPath();
126        ctx.moveTo(0, i * cellSize);
127        ctx.lineTo(canvas.width, i * cellSize);
128        ctx.stroke();
129    }
130    
131    // 绘制食物
132    ctx.fillStyle = 'red';
133    ctx.fillRect(food.x * cellSize, food.y * cellSize, cellSize, cellSize);
134    
135    // 绘制蛇
136    snake.forEach((segment, index) => {
137        ctx.fillStyle = index === 0 ? '#006400' : '#008000';
138        ctx.fillRect(segment.x * cellSize, segment.y * cellSize, cellSize, cellSize);
139    });
140}
141
142// 更新分数显示
143function updateScore() {
144    const scoreElement = document.getElementById('snake-score');
145    scoreElement.textContent = score;
146    
147    if (score > highScore) {
148        highScore = score;
149        const statusBar = document.querySelector('#snake-window .status-bar');
150        statusBar.children[1].textContent = `最高分:${highScore}`;
151    }
152}
153
154// 游戏结束
155function gameOver() {
156    clearInterval(gameInterval);
157    gameStarted = false;
158    
159    // 更新弹窗中的分数
160    document.getElementById('final-score').textContent = score;
161    document.getElementById('high-score').textContent = highScore;
162    
163    // 显示游戏结束弹窗
164    document.getElementById('game-over-dialog').style.display = 'block';
165}
166
167// 关闭游戏结束弹窗
168function closeGameOverDialog() {
169    document.getElementById('game-over-dialog').style.display = 'none';
170    initGame();
171}
172
173// 重新开始游戏
174function restartGame() {
175    document.getElementById('game-over-dialog').style.display = 'none';
176    initGame();
177    startSnakeGame();
178}
179
180// 开始游戏
181function startSnakeGame() {
182    if (!gameStarted) {
183        gameStarted = true;
184        isPaused = false;
185        initGame();
186        gameInterval = setInterval(updateGame, config.speed);
187    }
188}
189
190// 暂停游戏
191function pauseSnakeGame() {
192    if (gameStarted) {
193        isPaused = !isPaused;
194        const pauseButton = document.querySelector('#snake-window .control-buttons .win95-button:nth-child(2)');
195        pauseButton.textContent = isPaused ? '继续' : '暂停';
196    }
197}
198
199// 键盘控制
200function isTypingInEditable() {
201    const active = document.activeElement;
202    if (!active) return false;
203    const tag = active.tagName;
204    if (tag === 'INPUT' || tag === 'TEXTAREA') return true;
205    const ce = active.getAttribute && active.getAttribute('contenteditable');
206    return ce === '' || ce === 'true' || ce === 'plaintext-only';
207}
208
209document.addEventListener('keydown', (event) => {
210    if (typeof activeWindow !== 'undefined' && activeWindow !== 'snake') return;
211    if (isTypingInEditable()) return;
212    if (!gameStarted || isPaused) return;
213    
214    switch (event.key) {
215        case 'ArrowUp':
216            if (direction !== 'down') direction = 'up';
217            break;
218        case 'ArrowDown':
219            if (direction !== 'up') direction = 'down';
220            break;
221        case 'ArrowLeft':
222            if (direction !== 'right') direction = 'left';
223            break;
224        case 'ArrowRight':
225            if (direction !== 'left') direction = 'right';
226            break;
227    }
228});
229
230// 初始化游戏
231window.addEventListener('load', () => {
232    initGame();
233});

Line numbers count LF bytes from the start of the resource, as the search results do. Vendor segments are library code the classifier recognised; they are stored but not indexed. Bytes are shown as Latin1 characters, one per byte.