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);
});
相关推荐
liro1 小时前
HTML5和CSS3新增属性简要概括
前端
冴羽1 小时前
Svelte 最新中文文档翻译(1)—— 概述与入门指南
前端·javascript·vue.js·svelte·sveltekit
gqkmiss1 小时前
Chrome 132 版本新特性
前端·chrome·浏览器·chrome 132
颜酱2 小时前
element-ui实现动态表单点击按钮新增行/删除行
前端·javascript·vue.js
诸神缄默不语2 小时前
HTML中的`<!DOCTYPE html>`是什么意思?
前端·html
放逐者-保持本心,方可放逐2 小时前
HTML-BFC+SEO+标签应用实例
前端·html·seo·语义化标签·标签实例
Dontla2 小时前
React技术栈搭配(全栈)(MERN栈、PERN栈)
前端·react.js·前端框架
心.c2 小时前
vue知识点总结
前端·javascript·vue.js
程序员大澈2 小时前
每天一个技术知识:Nuxt服务端渲染原理
前端·javascript·vue.js
忘不了情3 小时前
react中,使用antd的Upload组件切片上传.zip文件及压缩包的下载
前端·react.js·前端框架