如何运行

  1. 创建文件:在你的电脑上创建三个文件:index.htmlstyle.cssscript.js
  2. 复制代码:将下面提供的HTML、CSS和JavaScript代码分别复制到对应的文件中。
  3. 打开网页:用浏览器打开 index.html 文件即可开始游戏。

HTML 代码 (index.html)

这个文件定义了游戏的基本结构,包括标题、分数显示和游戏面板。

网页版2048小游戏js代码
(图片来源网络,侵删)
<!DOCTYPE html>
<html lang="zh-CN">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">2048 游戏</title>
    <link rel="stylesheet" href="style.css">
</head>
<body>
    <div class="container">
        <header>
            <h1>2048</h1>
            <div class="scores-container">
                <div class="score-container">
                    <div class="score-title">分数</div>
                    <span id="score">0</span>
                </div>
                <div class="best-container">
                    <div class="score-title">最高分</div>
                    <span id="best-score">0</span>
                </div>
            </div>
        </header>
        <div class="game-container">
            <div class="game-message">
                <p></p>
                <div class="lower">
                    <a class="retry-button">新游戏</a>
                </div>
            </div>
            <div class="grid-container">
                <!-- 4x4 网格背景 -->
                <div class="grid-row">
                    <div class="grid-cell"></div>
                    <div class="grid-cell"></div>
                    <div class="grid-cell"></div>
                    <div class="grid-cell"></div>
                </div>
                <div class="grid-row">
                    <div class="grid-cell"></div>
                    <div class="grid-cell"></div>
                    <div class="grid-cell"></div>
                    <div class="grid-cell"></div>
                </div>
                <div class="grid-row">
                    <div class="grid-cell"></div>
                    <div class="grid-cell"></div>
                    <div class="grid-cell"></div>
                    <div class="grid-cell"></div>
                </div>
                <div class="grid-row">
                    <div class="grid-cell"></div>
                    <div class="grid-cell"></div>
                    <div class="grid-cell"></div>
                    <div class="grid-cell"></div>
                </div>
            </div>
            <!-- 动态生成的数字方块将放在这里 -->
            <div class="tile-container"></div>
        </div>
        <p class="game-explanation">
            <strong>玩法说明:</strong> 使用 <strong>方向键</strong> 移动方块,当两个相同数字的方块碰撞时,它们会 <strong>合并成一个!</strong>
        </p>
    </div>
    <script src="script.js"></script>
</body>
</html>

CSS 代码 (style.css)

这个文件负责游戏的视觉样式,包括布局、颜色和动画效果。

/* 基础样式 */
body {
    font-family: Arial, sans-serif;
    background-color: #faf8ef;
    color: #776e65;
    margin: 0;
    padding: 0;
}
.container {
    width: 500px;
    margin: 0 auto;
    padding: 20px;
}
header {
    display: flex;
    justify-content: space-between;
    align-items: center;
    margin-bottom: 20px;
}
h1 {
    font-size: 80px;
    font-weight: bold;
    margin: 0;
    color: #776e65;
}
/* 分数样式 */
.scores-container {
    display: flex;
    gap: 10px;
}
.score-container, .best-container {
    position: relative;
    background: #bbada0;
    padding: 15px 25px;
    border-radius: 3px;
    color: white;
    text-align: center;
    min-width: 60px;
}
.score-title {
    font-size: 13px;
    text-transform: uppercase;
}
#score, #best-score {
    font-size: 25px;
    font-weight: bold;
}
/* 游戏区域 */
.game-container {
    position: relative;
    background: #bbada0;
    border-radius: 6px;
    width: 500px;
    height: 500px;
    padding: 15px;
    box-sizing: border-box;
}
.grid-container {
    position: absolute;
    z-index: 1;
}
.grid-row {
    display: flex;
    margin-bottom: 15px;
}
.grid-row:last-child {
    margin-bottom: 0;
}
.grid-cell {
    width: 106.25px;
    height: 106.25px;
    margin-right: 15px;
    background: rgba(238, 228, 218, 0.35);
    border-radius: 3px;
}
.grid-cell:last-child {
    margin-right: 0;
}
/* 数字方块 */
.tile-container {
    position: absolute;
    z-index: 2;
}
.tile {
    position: absolute;
    width: 106.25px;
    height: 106.25px;
    font-size: 55px;
    font-weight: bold;
    line-height: 106.25px;
    text-align: center;
    border-radius: 3px;
    transition: all 0.15s ease-in-out;
}
/* 不同数字的颜色 */
.tile-2 { background: #eee4da; color: #776e65; }
.tile-4 { background: #ede0c8; color: #776e65; }
.tile-8 { background: #f2b179; color: #f9f6f2; }
.tile-16 { background: #f59563; color: #f9f6f2; }
.tile-32 { background: #f67c5f; color: #f9f6f2; }
.tile-64 { background: #f65e3b; color: #f9f6f2; }
.tile-128 { background: #edcf72; color: #f9f6f2; font-size: 45px; }
.tile-256 { background: #edcc61; color: #f9f6f2; font-size: 45px; }
.tile-512 { background: #edc850; color: #f9f6f2; font-size: 45px; }
.tile-1024 { background: #edc53f; color: #f9f6f2; font-size: 35px; }
.tile-2048 { background: #edc22e; color: #f9f6f2; font-size: 35px; }
/* 新方块和合并动画 */
.tile-new {
    animation: appear 0.2s ease-in-out;
}
.tile-merged {
    animation: pop 0.2s ease-in-out;
    z-index: 20;
}
@keyframes appear {
    0% {
        opacity: 0;
        transform: scale(0);
    }
    100% {
        opacity: 1;
        transform: scale(1);
    }
}
@keyframes pop {
    0% { transform: scale(1); }
    50% { transform: scale(1.2); }
    100% { transform: scale(1); }
}
/* 游戏消息(胜利/失败) */
.game-message {
    display: none;
    position: absolute;
    top: 0;
    left: 0;
    width: 100%;
    height: 100%;
    background: rgba(238, 228, 218, 0.73);
    z-index: 100;
    text-align: center;
    flex-direction: column;
    justify-content: center;
    align-items: center;
    border-radius: 6px;
}
.game-message p {
    font-size: 60px;
    font-weight: bold;
    margin-bottom: 20px;
    color: #776e65;
}
.game-message.game-won {
    display: flex;
    background: rgba(237, 194, 46, 0.5);
}
.game-message.game-over {
    display: flex;
}
.retry-button {
    display: inline-block;
    background: #8f7a66;
    border-radius: 3px;
    padding: 0 20px;
    text-decoration: none;
    color: #f9f6f2;
    height: 40px;
    line-height: 42px;
    font-weight: bold;
    cursor: pointer;
}
/* 响应式设计 */
@media screen and (max-width: 520px) {
    .container {
        width: 320px;
        padding: 10px;
    }
    h1 {
        font-size: 50px;
    }
    .game-container, .grid-cell, .tile {
        width: 70px;
        height: 70px;
    }
    .grid-row {
        margin-bottom: 10px;
    }
    .grid-cell {
        width: 60px;
        height: 60px;
        margin-right: 10px;
    }
    .tile {
        font-size: 35px;
        line-height: 70px;
    }
    .tile-128, .tile-256, .tile-512 {
        font-size: 30px;
    }
    .tile-1024, .tile-2048 {
        font-size: 25px;
    }
}

JavaScript 代码 (script.js)

这是游戏的核心逻辑,包含了所有功能:初始化、移动、合并、计分和判断游戏状态。

document.addEventListener('DOMContentLoaded', () => {
    // 游戏状态
    let grid = [];
    let score = 0;
    let bestScore = localStorage.getItem('bestScore') || 0;
    let gameOver = false;
    let gameWon = false;
    // DOM 元素
    const scoreDisplay = document.getElementById('score');
    const bestScoreDisplay = document.getElementById('best-score');
    const messageDisplay = document.querySelector('.game-message');
    const retryButton = document.querySelector('.retry-button');
    const tileContainer = document.querySelector('.tile-container');
    // 初始化最高分显示
    bestScoreDisplay.textContent = bestScore;
    // 初始化游戏
    function init() {
        grid = Array(4).fill().map(() => Array(4).fill(0));
        score = 0;
        gameOver = false;
        gameWon = false;
        scoreDisplay.textContent = score;
        messageDisplay.classList.remove('game-won', 'game-over');
        messageDisplay.style.display = 'none';
        clearTiles();
        addNewTile();
        addNewTile();
        updateDisplay();
    }
    // 清除所有数字方块
    function clearTiles() {
        tileContainer.innerHTML = '';
    }
    // 在空白格子中随机添加一个新方块 (2或4)
    function addNewTile() {
        const emptyCells = [];
        for (let r = 0; r < 4; r++) {
            for (let c = 0; c < 4; c++) {
                if (grid[r][c] === 0) {
                    emptyCells.push({ r, c });
                }
            }
        }
        if (emptyCells.length > 0) {
            const randomCell = emptyCells[Math.floor(Math.random() * emptyCells.length)];
            grid[randomCell.r][randomCell.c] = Math.random() < 0.9 ? 2 : 4;
        }
    }
    // 更新显示
    function updateDisplay() {
        clearTiles();
        for (let r = 0; r < 4; r++) {
            for (let c = 0; c < 4; c++) {
                if (grid[r][c] !== 0) {
                    const tile = document.createElement('div');
                    tile.className = `tile tile-${grid[r][c]}`;
                    tile.textContent = grid[r][c];
                    tile.style.transform = `translate(${c * 121}px, ${r * 121}px)`; // 106.25 + 15
                    tileContainer.appendChild(tile);
                }
            }
        }
    }
    // 移动和合并逻辑
    function move(direction) {
        if (gameOver) return;
        let moved = false;
        const previousGrid = grid.map(row => [...row]);
        if (direction === 'ArrowLeft' || direction === 'ArrowRight') {
            for (let r = 0; r < 4; r++) {
                let row = grid[r];
                if (direction === 'ArrowRight') row.reverse();
                row = slideAndMerge(row);
                if (direction === 'ArrowRight') row.reverse();
                grid[r] = row;
            }
        } else {
            for (let c = 0; c < 4; c++) {
                let column = [grid[0][c], grid[1][c], grid[2][c], grid[3][c]];
                if (direction === 'ArrowDown') column.reverse();
                column = slideAndMerge(column);
                if (direction === 'ArrowDown') column.reverse();
                for (let r = 0; r < 4; r++) {
                    grid[r][c] = column[r];
                }
            }
        }
        // 检查是否有移动发生
        for (let r = 0; r < 4; r++) {
            for (let c = 0; c < 4; c++) {
                if (grid[r][c] !== previousGrid[r][c]) {
                    moved = true;
                    break;
                }
            }
        }
        if (moved) {
            addNewTile();
            updateDisplay();
            checkGameState();
        }
    }
    // 滑动和合并一行/列
    function slideAndMerge(line) {
        // 移除零
        let newLine = line.filter(val => val !== 0);
        // 合并相同的数字
        for (let i = 0; i < newLine.length - 1; i++) {
            if (newLine[i] === newLine[i + 1]) {
                newLine[i] *= 2;
                score += newLine[i];
                scoreDisplay.textContent = score;
                newLine.splice(i + 1, 1);
            }
        }
        // 补充零
        while (newLine.length < 4) {
            newLine.push(0);
        }
        return newLine;
    }
    // 检查游戏状态
    function checkGameState() {
        // 检查是否获胜
        if (!gameWon) {
            for (let r = 0; r < 4; r++) {
                for (let c = 0; c < 4; c++) {
                    if (grid[r][c] === 2048) {
                        gameWon = true;
                        messageDisplay.querySelector('p').textContent = '你赢了!';
                        messageDisplay.classList.add('game-won');
                        messageDisplay.style.display = 'flex';
                        return;
                    }
                }
            }
        }
        // 检查是否还有空格
        for (let r = 0; r < 4; r++) {
            for (let c = 0; c < 4; c++) {
                if (grid[r][c] === 0) {
                    return; // 游戏继续
                }
            }
        }
        // 检查是否还能移动
        for (let r = 0; r < 4; r++) {
            for (let c = 0; c < 4; c++) {
                const current = grid[r][c];
                if (
                    (r < 3 && grid[r + 1][c] === current) ||
                    (c < 3 && grid[r][c + 1] === current)
                ) {
                    return; // 游戏继续
                }
            }
        }
        // 如果以上都不满足,则游戏结束
        gameOver = true;
        messageDisplay.querySelector('p').textContent = '游戏结束!';
        messageDisplay.classList.add('game-over');
        messageDisplay.style.display = 'flex';
        // 更新最高分
        if (score > bestScore) {
            bestScore = score;
            localStorage.setItem('bestScore', bestScore);
            bestScoreDisplay.textContent = bestScore;
        }
    }
    // 事件监听
    document.addEventListener('keydown', (e) => {
        if (['ArrowUp', 'ArrowDown', 'ArrowLeft', 'ArrowRight'].includes(e.key)) {
            e.preventDefault();
            move(e.key);
        }
    });
    retryButton.addEventListener('click', init);
    // 启动游戏
    init();
});

代码逻辑解析

  1. HTML 结构:

    • 一个主容器 container 包含所有内容。
    • header 显示游戏标题和分数。
    • game-container 是游戏的核心区域,包含一个静态的背景网格 grid-container 和一个动态的数字方块容器 tile-container,数字方块会通过 JavaScript 动态添加到 tile-container 中。
    • game-message 用于显示“你赢了!”或“游戏结束!”的提示,默认是隐藏的。
  2. CSS 样式:

    网页版2048小游戏js代码
    (图片来源网络,侵删)
    • 使用 Flexbox 布局来排列标题和分数。
    • game-container 设置为 position: relative,内部的 grid-containertile-container 都使用 position: absolute 并设置 z-index,使数字方块可以显示在背景网格之上。
    • 为不同数值的方块定义了不同的背景颜色。
    • 使用 CSS transition@keyframes 动画,让新方块的出现和方块的合并过程更平滑。
  3. JavaScript 逻辑:

    • 初始化 (init):
      • 创建一个 4x4 的二维数组 grid,初始值全为 0。
      • 重置分数和游戏状态。
      • 调用 addNewTile() 两次,在空白位置随机生成两个新方块(90% 概率是 2,10% 概率是 4)。
      • 调用 updateDisplay()grid 数组中的数据渲染到页面上。
    • 移动 (move):
      • 根据按下的方向键(ArrowLeft, ArrowRight, ArrowUp, ArrowDown)决定处理行还是列。
      • 为了简化逻辑,ArrowRightArrowDown 会对行或列进行反转,然后统一当作向左或向上处理,处理完再反转回来。
      • 对每一行/列调用 slideAndMerge() 函数。
      • 如果移动后有变化,则生成一个新方块并更新显示。
    • 滑动与合并 (slideAndMerge):
      • 这是核心算法,它接收一行或一列的数组。
      • 第一步:过滤掉所有的 0,将非零数字靠左/靠上对齐。
      • 第二步:从左到右/从上到下遍历,如果相邻的两个数字相等,就将它们合并成一个(值翻倍),并增加分数,然后移除被合并掉的数字。
      • 第三步:再次过滤掉 0,确保合并后的数字紧挨着,然后在末尾补 0,使数组长度保持为 4。
    • 游戏状态检查 (checkGameState):
      • 检查是否出现了 2048 这个数字,如果出现了,玩家获胜。
      • 检查是否还有 0,如果有,说明游戏还能继续。
      • 如果没有 0,则检查是否还有相邻的相同数字,如果有,说明还能合并,游戏继续。
      • 如果以上条件都不满足,则判定游戏结束。
    • 事件监听:
      • 监听键盘的 keydown 事件,当按下方向键时触发 move 函数。
      • 监听“新游戏”按钮的点击事件,触发 init 函数重新开始。

这个实现涵盖了2048游戏的所有基本规则和交互,代码结构良好,易于扩展。

网页版2048小游戏js代码
(图片来源网络,侵删)