JavaScript Canvas 生命游戏实现:原生JS 100行代码模拟细胞演化(附在线Demo)

📅 2026/7/13 12:56:37
JavaScript Canvas 生命游戏实现:原生JS 100行代码模拟细胞演化(附在线Demo)
JavaScript Canvas 生命游戏实现原生JS 100行代码模拟细胞演化在计算机科学领域康威生命游戏是一个经典的零玩家游戏它展示了简单的规则如何产生复杂的模式。本文将带你用原生JavaScript和Canvas API实现这个迷人的细胞自动机仅用约100行代码就能创建完整的交互式模拟。1. 生命游戏基础与规则设计生命游戏在一个二维网格上进行每个格子代表一个细胞有两种状态存活黑色或死亡白色。细胞的下一代状态由以下四条规则决定孤独死亡活细胞周围少于2个活细胞时下一代死亡稳定存活活细胞周围有2或3个活细胞时继续存活过度拥挤活细胞周围超过3个活细胞时下一代死亡繁殖死细胞周围恰好有3个活细胞时下一代复活// 规则实现核心逻辑 function applyRules(current, neighbors) { if (current 1) { return (neighbors 2 || neighbors 3) ? 1 : 0; } else { return neighbors 3 ? 1 : 0; } }关键数据结构我们使用二维数组表示细胞状态0表示死亡1表示存活。这种表示方式既节省内存又便于计算邻居数量。2. Canvas初始化与网格绘制首先设置Canvas元素并获取绘图上下文canvas idgameCanvas width500 height500/canvasconst canvas document.getElementById(gameCanvas); const ctx canvas.getContext(2d); const size 20; // 每个细胞的大小(像素) const rows Math.floor(canvas.height / size); const cols Math.floor(canvas.width / size); // 初始化随机细胞状态 function createGrid() { return Array.from({length: rows}, () Array.from({length: cols}, () Math.random() 0.7 ? 1 : 0 ) ); } let grid createGrid();绘制网格的函数需要考虑性能优化使用requestAnimationFrame实现动画循环function drawGrid() { ctx.clearRect(0, 0, canvas.width, canvas.height); for (let i 0; i rows; i) { for (let j 0; j cols; j) { ctx.beginPath(); ctx.rect(j * size, i * size, size, size); ctx.fillStyle grid[i][j] ? black : white; ctx.fill(); ctx.stroke(); } } requestAnimationFrame(drawGrid); }3. 邻居计数与状态更新算法计算每个细胞的活邻居数量是核心算法需要考虑边界条件function countNeighbors(grid, x, y) { let sum 0; for (let i -1; i 2; i) { for (let j -1; j 2; j) { // 跳过自身 if (i 0 j 0) continue; const row (x i rows) % rows; const col (y j cols) % cols; sum grid[row][col]; } } return sum; }状态更新需要双缓冲技术避免计算过程中的状态污染function updateGrid() { const newGrid Array.from({length: rows}, () new Array(cols).fill(0)); for (let i 0; i rows; i) { for (let j 0; j cols; j) { const neighbors countNeighbors(grid, i, j); newGrid[i][j] applyRules(grid[i][j], neighbors); } } grid newGrid; }4. 交互功能与性能优化为提升用户体验我们添加以下交互功能// 点击切换细胞状态 canvas.addEventListener(click, (e) { const x Math.floor(e.offsetX / size); const y Math.floor(e.offsetY / size); grid[y][x] grid[y][x] ? 0 : 1; }); // 空格键暂停/继续 let isRunning true; document.addEventListener(keydown, (e) { if (e.code Space) isRunning !isRunning; }); // 随机初始化按钮 document.getElementById(randomize).addEventListener(click, () { grid createGrid(); });性能优化技巧使用setInterval控制演化速度采用位运算替代条件判断减少DOM操作频率// 主循环 setInterval(() { if (isRunning) { updateGrid(); drawGrid(); } }, 100);5. 进阶扩展与模式示例生命游戏中有许多著名模式我们可以预定义一些const patterns { glider: [ [0,1,0], [0,0,1], [1,1,1] ], blinker: [ [1,1,1] ] }; function placePattern(pattern, x, y) { for (let i 0; i pattern.length; i) { for (let j 0; j pattern[0].length; j) { const row (y i) % rows; const col (x j) % cols; grid[row][col] pattern[i][j]; } } }双缓冲优化的完整实现let gridBuffer [createGrid(), createGrid()]; let currentBuffer 0; function swapBuffers() { currentBuffer 1 - currentBuffer; } function getCurrentGrid() { return gridBuffer[currentBuffer]; } function getNextGrid() { return gridBuffer[1 - currentBuffer]; }6. 完整实现与在线演示将所有部分组合起来完整的实现不到100行代码。以下是核心逻辑的完整版本// 初始化 const canvas document.getElementById(gameCanvas); const ctx canvas.getContext(2d); const size 10; const rows canvas.height / size; const cols canvas.width / size; let grid Array.from({length: rows}, () Array.from({length: cols}, () Math.random() 0.7 ? 1 : 0) ); // 主循环 function gameLoop() { updateGrid(); drawGrid(); requestAnimationFrame(gameLoop); } // 启动 gameLoop();点击这里体验在线Demo 实际项目中替换为真实链接在实际项目中我发现在Canvas渲染时关闭网格边框能显著提升性能特别是在高分辨率网格上。另外使用TypedArray代替常规数组可以进一步优化内存使用和计算速度。