用原生 HTML+CSS+JS 写一个 DOM 版 2048:方向键滑动、相同数字合并、随机生成 2/4、游戏结束判断。不依赖任何框架,浏览器打开即玩。
1. 页面结构
用 4×4 的 <div> 网格做棋盘,每个格子一个 <div>,数字和背景色都通过 JS 更新:
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8"/>
<title>2048</title>
<style>
body{font-family:sans-serif;text-align:center;background:#222;color:#eee}
#board{display:grid;grid-template-columns:repeat(4,100px);gap:8px;
width:424px;margin:20px auto;background:#bbada0;padding:8px;border-radius:8px}
.cell{width:100px;height:100px;border-radius:6px;background:#cdc1b4;
display:flex;align-items:center;justify-content:center;
font-size:34px;font-weight:bold;color:#776e65}
</style>
</head>
<body>
<h1>2048</h1>
<div id="board"></div>
<div id="info"></div>
<script>
// 逻辑写这里
</script>
</body>
</html>
2. 滑动合并
把一行抽出来:去 0 → 相邻相同合并 → 补 0。四个方向用"反转/转置"复用同一个函数:
function slide(line) {
let a = line.filter(v => v !== 0), b = [];
for (let i = 0; i < a.length; i++) {
if (i + 1 < a.length && a[i] === a[i + 1]) {
b.push(a[i] * 2); score += a[i] * 2; i++;
} else b.push(a[i]);
}
while (b.length < 4) b.push(0);
return b;
}
function moveGrid(dir) { // 0左 1右 2上 3下
for (let i = 0; i < 4; i++) {
let line = dir < 2 ? grid[i].slice() : grid.map(r => r[i]);
if (dir === 1 || dir === 3) line.reverse();
line = slide(line);
if (dir === 1 || dir === 3) line.reverse();
for (let j = 0; j < 4; j++) {
if (dir < 2) grid[i][j] = line[j];
else grid[j][i] = line[j];
}
}
spawn(); render();
}
3. 完整代码
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8"/>
<title>2048</title>
<style>
body{font-family:sans-serif;text-align:center;background:#222;color:#eee}
#board{display:grid;grid-template-columns:repeat(4,100px);gap:8px;
width:424px;margin:20px auto;background:#bbada0;padding:8px;border-radius:8px}
.cell{width:100px;height:100px;border-radius:6px;background:#cdc1b4;
display:flex;align-items:center;justify-content:center;
font-size:34px;font-weight:bold;color:#776e65}
</style>
</head>
<body>
<h1>2048 <small>方向键移动</small></h1>
<div id="board"></div>
<div id="info"></div>
<script>
const COLORS = {2:'#eee4da',4:'#ede0c8',8:'#f2b179',16:'#f59563',32:'#f67c5f',
64:'#f65e3b',128:'#edcf72',256:'#edcc61',512:'#edc850',1024:'#edc53f',2048:'#edc22e'};
let grid = Array.from({length:4}, () => Array(4).fill(0));
let score = 0;
const board = document.getElementById('board');
for (let i = 0; i < 16; i++) {
const d = document.createElement('div');
d.className = 'cell';
board.appendChild(d);
}
function slide(line) {
let a = line.filter(v => v !== 0), b = [];
for (let i = 0; i < a.length; i++) {
if (i + 1 < a.length && a[i] === a[i + 1]) { b.push(a[i] * 2); score += a[i] * 2; i++; }
else b.push(a[i]);
}
while (b.length < 4) b.push(0);
return b;
}
function moveGrid(dir) {
for (let i = 0; i < 4; i++) {
let line = dir < 2 ? grid[i].slice() : grid.map(r => r[i]);
if (dir === 1 || dir === 3) line.reverse();
line = slide(line);
if (dir === 1 || dir === 3) line.reverse();
for (let j = 0; j < 4; j++) {
if (dir < 2) grid[i][j] = line[j];
else grid[j][i] = line[j];
}
}
spawn(); render();
}
function spawn() {
const empty = [];
for (let r = 0; r < 4; r++) for (let c = 0; c < 4; c++)
if (!grid[r][c]) empty.push([r, c]);
if (!empty.length) return;
const [r, c] = empty[Math.floor(Math.random() * empty.length)];
grid[r][c] = Math.random() < 0.9 ? 2 : 4;
}
function canMove() {
for (let r = 0; r < 4; r++) for (let c = 0; c < 4; c++) {
if (!grid[r][c]) return true;
if (c + 1 < 4 && grid[r][c] === grid[r][c + 1]) return true;
if (r + 1 < 4 && grid[r][c] === grid[r + 1][c]) return true;
}
return false;
}
function render() {
const cells = board.children;
for (let i = 0; i < 16; i++) {
const v = grid[Math.floor(i / 4)][i % 4];
cells[i].textContent = v || '';
cells[i].style.background = COLORS[v] || '#cdc1b4';
cells[i].style.color = v > 4 ? '#fff' : '#776e65';
}
document.getElementById('info').textContent = '分数: ' + score;
if (!canMove()) alert('游戏结束!分数: ' + score);
}
document.addEventListener('keydown', e => {
const map = {ArrowLeft:0, ArrowRight:1, ArrowUp:2, ArrowDown:3};
if (e.key in map) { e.preventDefault(); moveGrid(map[e.key]); }
});
spawn(); spawn(); render();
</script>
</body>
</html>
4. 运行与常见问题
保存为 2048.html 用浏览器打开。常见问题:①方向键滚页面——e.preventDefault();②新块每次都生成——moveGrid 后无条件 spawn,想做"有效才生成"就对比滑动前后;③手机玩不了——加触摸事件。
💡 改进方向:①触摸滑动;②撤销;③最高分存 localStorage;④动画过渡;⑤移动端自适应。