canvas snake game

在线演示
play

移动原理

代码

html 复制代码
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Snake Game</title>
    <link rel="stylesheet" href="style.css">
</head>
<body>
    <div class="game-container">
        <canvas id="gameCanvas"></canvas>
        <div class="score" id="score">Score: 0</div>
        <div class="game-over" id="gameOver">Game Over!<br><button onclick="restartGame()">Restart</button></div>
    </div>

    <script src="script.js"></script>
</body>
</html>
css 复制代码
* {
    margin: 0;
    padding: 0;
    box-sizing: border-box;
}

body {
    font-family: 'Arial', sans-serif;
    background: radial-gradient(circle, #1e3c72, #2a5298);
    color: #fff;
    display: flex;
    justify-content: center;
    align-items: center;
    height: 100vh;
    overflow: hidden;
}

.game-container {
    position: relative;
    width: 90vw;
    height: 90vw;
    max-width: 600px;
    max-height: 600px;
    background: linear-gradient(135deg, #0d1117, #161b22);
    border-radius: 20px;
    overflow: hidden;
    box-shadow: 0 10px 20px rgba(0, 0, 0, 0.5);
}

canvas {
    width: 100%;
    height: 100%;
    display: block;
}

.score {
    position: absolute;
    top: 10px;
    left: 10px;
    background: rgba(255, 255, 255, 0.1);
    padding: 10px 15px;
    border-radius: 5px;
    font-size: 18px;
    box-shadow: 0 4px 6px rgba(0, 0, 0, 0.3);
}

.game-over {
    position: absolute;
    top: 50%;
    left: 50%;
    transform: translate(-50%, -50%);
    background: rgba(0, 0, 0, 0.9);
    padding: 30px 50px;
    border-radius: 20px;
    text-align: center;
    font-size: 24px;
    color: #fff;
    display: none;
    box-shadow: 0 8px 15px rgba(0, 0, 0, 0.6);
}

.game-over button {
    margin-top: 15px;
    padding: 10px 20px;
    background: #e63946;
    color: #fff;
    border: none;
    border-radius: 10px;
    font-size: 18px;
    cursor: pointer;
    transition: background 0.3s ease;
}

.game-over button:hover {
    background: #d62839;
}
javascript 复制代码
const canvas = document.getElementById('gameCanvas');
const ctx = canvas.getContext('2d');
canvas.width = canvas.height = Math.min(window.innerWidth * 0.9, 600);

const box = canvas.width / 30;
let score = 0;
let speed = 150;
let snake = [{ x: box * 5, y: box * 5 }];
let direction = 'RIGHT';
let food = generateFood();
let gameLoop;

function generateFood() {
    let foodX, foodY;
    do {
        foodX = Math.floor((Math.random() * canvas.width) / box) * box;
        foodY = Math.floor((Math.random() * canvas.height) / box) * box;
    } while (snake.some(segment => segment.x === foodX && segment.y === foodY));
    return { x: foodX, y: foodY };
}

document.addEventListener('keydown', changeDirection);

function changeDirection(event) {
    const key = event.key;
    const oppositeDirections = {
        UP: 'DOWN',
        DOWN: 'UP',
        LEFT: 'RIGHT',
        RIGHT: 'LEFT'
    };
    const newDirection = {
        ArrowUp: 'UP',
        ArrowDown: 'DOWN',
        ArrowLeft: 'LEFT',
        ArrowRight: 'RIGHT'
    }[key];
    if (newDirection && newDirection !== oppositeDirections[direction]) {
        direction = newDirection;
    }
}

function drawSnake() {
    snake.forEach((segment, index) => {
        ctx.fillStyle = index === 0 ? '#00ff88' : '#00cc66';
        ctx.fillRect(segment.x, segment.y, box, box);
        ctx.strokeStyle = '#161b22';
        ctx.strokeRect(segment.x, segment.y, box, box);
    });
}

function drawFood() {
    ctx.fillStyle = '#ff0033';
    ctx.beginPath();
    ctx.arc(food.x + box / 2, food.y + box / 2, box / 2.5, 0, Math.PI * 2);
    ctx.fill();
}

function moveSnake() {
    const head = { ...snake[0] };
    if (direction === 'UP') head.y -= box;
    else if (direction === 'DOWN') head.y += box;
    else if (direction === 'LEFT') head.x -= box;
    else if (direction === 'RIGHT') head.x += box;

    snake.unshift(head);

    if (head.x === food.x && head.y === food.y) {
        score += 10;
        document.getElementById('score').innerText = `Score: ${score}`;
        food = generateFood();
        if (speed > 50) speed -= 5;
        clearInterval(gameLoop);
        gameLoop = setInterval(update, speed);
    } else {
        snake.pop();
    }
}

function checkCollision() {
    const head = snake[0];
    if (
        head.x < 0 ||
        head.x >= canvas.width ||
        head.y < 0 ||
        head.y >= canvas.height ||
        snake.slice(1).some(segment => segment.x === head.x && segment.y === head.y)
    ) {
        document.getElementById('gameOver').style.display = 'block';
        clearInterval(gameLoop);
    }
}

function restartGame() {
    snake = [{ x: box * 5, y: box * 5 }];
    direction = 'RIGHT';
    score = 0;
    speed = 150;
    document.getElementById('score').innerText = `Score: 0`;
    document.getElementById('gameOver').style.display = 'none';
    food = generateFood();
    gameLoop = setInterval(update, speed);
}

function update() {
    ctx.clearRect(0, 0, canvas.width, canvas.height);
    drawFood();
    moveSnake();
    drawSnake();
    checkCollision();
}

gameLoop = setInterval(update, speed);

window.addEventListener('resize', () => {
    canvas.width = canvas.height = Math.min(window.innerWidth * 0.9, 600);
});
相关推荐
雪芽蓝域zzs几秒前
第三十五节:Axios 统一错误拦截、401 Token 过期处理
前端·javascript·vue.js
XIE3926 分钟前
TipKit:开源富文本编辑器套件,一套逻辑,任意风格!
前端·笔记·开源
范什么特西20 分钟前
一些常用名词
开发语言·前端·javascript
晴天1621 分钟前
前端打包工具全景解析
前端
晴天1623 分钟前
Vite vs Webpack 全方位对比
前端·webpack·node.js
体毛旺盛的猿25 分钟前
HarmonyOS开发面试题
前端·华为·harmonyos
anOnion40 分钟前
构建无障碍组件之Landmarks Pattern
前端·html·交互设计
牧瀬クリスだ41 分钟前
前端三件套基础之JS!!!!
前端·javascript
风骏时光牛马44 分钟前
提示词工程:大模型交互的核心塑造技术
前端
单线程_011 小时前
从案例分析 Vue3 Tokenizer+Parser 源码四
前端·javascript·vue.js