如何使用Vanilla JavaScript获取二维数组中的相邻单元格?
解决网格单元格获取相邻单元格的问题
你的代码核心问题在于Cell对象只保存了像素坐标,没有记录它在网格中的**行(row)和列(col)**位置,导致无法通过网格的二维数组索引定位相邻单元格。下面一步步修正并实现邻居获取逻辑:
步骤1:修改Cell构造函数,添加行列属性
给每个Cell绑定网格内的行列索引,同时直接通过行列计算像素坐标,简化后续代码:
function Cell(row, col) { // 网格内的行列位置(关键!) this.row = row; this.col = col; // 像素坐标直接通过行列计算 this.x = col * cellSize; this.y = row * cellSize; this.f = 0; this.g = 0; this.h = 0; // 记录父节点,用于后续路径回溯 this.parent = null; this.show = function(color = 'gray') { ctx.fillStyle = color; ctx.fillRect(this.x, this.y, cellSize, cellSize); ctx.strokeStyle = 'white'; ctx.strokeRect(this.x, this.y, cellSize, cellSize); } }
步骤2:实现获取相邻单元格的方法
给Cell添加原型方法getNeighbors,遍历上下左右四个方向(可扩展对角线),同时做边界检查避免超出网格范围:
Cell.prototype.getNeighbors = function() { const neighbors = []; // 上下左右四个方向的偏移量 const directions = [ [-1, 0], // 上 [1, 0], // 下 [0, -1], // 左 [0, 1] // 右 // 需要对角线邻居的话,取消注释下面的行 // [-1, -1], [-1, 1], [1, -1], [1, 1] ]; for (const [rowOffset, colOffset] of directions) { const newRow = this.row + rowOffset; const newCol = this.col + colOffset; // 检查是否在网格有效范围内 if (newRow >= 0 && newRow < rows && newCol >= 0 && newCol < cols) { neighbors.push(grid[newRow][newCol]); } } return neighbors; }
步骤3:修正createGrid函数
创建Cell时直接传入行列索引,简化像素坐标的赋值逻辑:
function createGrid() { for (let i = 0; i < rows; i++) { grid[i] = new Array(cols); } for (let i = 0; i < rows; i++) { for (let j = 0; j < cols; j++) { grid[i][j] = new Cell(i, j); grid[i][j].show(); } } }
步骤4:在update中结合A*逻辑使用邻居方法
完善update函数,加入A*算法的核心流程,其中就会用到获取邻居的逻辑:
// 把interval移到全局,方便后续停止动画 let interval; function update() { // open列表为空,说明无路径 if (open.length === 0) { console.log('No path found'); clearInterval(interval); return; } // 找到open列表中f值最小的节点 let currentIndex = 0; for (let i = 0; i < open.length; i++) { if (open[i].f < open[currentIndex].f) { currentIndex = i; } } const current = open[currentIndex]; // 到达终点,回溯路径并停止 if (current === end) { console.log('Path found!'); let path = []; let temp = current; while (temp.parent) { path.push(temp); temp = temp.parent; } path.push(start); // 绘制路径 path.forEach(cell => cell.show('blue')); clearInterval(interval); return; } // 将当前节点从open移到closed removeArray(open, current); closed.push(current); // 遍历当前节点的所有邻居 const neighbors = current.getNeighbors(); for (const neighbor of neighbors) { // 邻居在closed列表中,跳过 if (closed.includes(neighbor)) continue; const tempG = current.g + 1; // 每步代价设为1 let newPath = false; // 邻居不在open列表,添加进去 if (!open.includes(neighbor)) { open.push(neighbor); newPath = true; } else if (tempG < neighbor.g) { // 当前路径到邻居的代价更小,更新路径 newPath = true; } if (newPath) { neighbor.parent = current; neighbor.g = tempG; // 曼哈顿距离计算h值(适合网格场景) neighbor.h = Math.abs(neighbor.col - end.col) + Math.abs(neighbor.row - end.row); neighbor.f = neighbor.g + neighbor.h; } } // 重绘网格避免颜色重叠 for (let i = 0; i < rows; i++) { for (let j = 0; j < cols; j++) { grid[i][j].show(); } } // 重新标记起点终点 start.show('yellow'); end.show('purple'); // 绘制open和closed列表 for (let i = 0; i < open.length; i++) { open[i].show('green'); } for (let i = 0; i < closed.length; i++) { closed[i].show('red'); } }
完整修改后的代码
//GLOBAL VARIABLES const canvas = document.createElement('canvas'); const ctx = canvas.getContext('2d'); const wh = 600; const cellSize = 30; const rows = 20; const cols = 20; const grid = new Array(rows); const open = []; const closed = []; let start; let end; let interval; //FUNCTIONS (function() { setup(); })(); function Cell(row, col) { this.row = row; this.col = col; this.x = col * cellSize; this.y = row * cellSize; this.f = 0; this.g = 0; this.h = 0; this.parent = null; this.show = function(color = 'gray') { ctx.fillStyle = color; ctx.fillRect(this.x, this.y, cellSize, cellSize); ctx.strokeStyle = 'white'; ctx.strokeRect(this.x, this.y, cellSize, cellSize); } } Cell.prototype.getNeighbors = function() { const neighbors = []; const directions = [ [-1, 0], [1, 0], [0, -1], [0, 1] // [-1, -1], [-1, 1], [1, -1], [1, 1] ]; for (const [rowOffset, colOffset] of directions) { const newRow = this.row + rowOffset; const newCol = this.col + colOffset; if (newRow >= 0 && newRow < rows && newCol >=0 && newCol < cols) { neighbors.push(grid[newRow][newCol]); } } return neighbors; } function setup() { interval = setInterval(update, 120); canvas.setAttribute('width', wh); canvas.setAttribute('height', wh); document.body.insertBefore(canvas, document.body.childNodes[0]); createGrid(); setStartEnd(); } function createGrid() { for (let i = 0; i < rows; i++) { grid[i] = new Array(cols); } for (let i = 0; i < rows; i++) { for (let j = 0; j < cols; j++) { grid[i][j] = new Cell(i, j); grid[i][j].show(); } } } function setStartEnd() { start = grid[0][0]; end = grid[cols - 1][rows - 1]; open.push(start); start.show('yellow'); end.show('purple'); } function removeArray(arr, e) { for (let i = 0; i < arr.length; i++) { if (arr[i] === e) { arr.splice(i, 1); break; } } } function update() { if (open.length === 0) { console.log('No path found'); clearInterval(interval); return; } let currentIndex = 0; for (let i = 0; i < open.length; i++) { if (open[i].f < open[currentIndex].f) { currentIndex = i; } } const current = open[currentIndex]; if (current === end) { console.log('Path found!'); let path = []; let temp = current; while (temp.parent) { path.push(temp); temp = temp.parent; } path.push(start); path.forEach(cell => cell.show('blue')); clearInterval(interval); return; } removeArray(open, current); closed.push(current); const neighbors = current.getNeighbors(); for (const neighbor of neighbors) { if (closed.includes(neighbor)) continue; const tempG = current.g + 1; let newPath = false; if (!open.includes(neighbor)) { open.push(neighbor); newPath = true; } else if (tempG < neighbor.g) { newPath = true; } if (newPath) { neighbor.parent = current; neighbor.g = tempG; neighbor.h = Math.abs(neighbor.col - end.col) + Math.abs(neighbor.row - end.row); neighbor.f = neighbor.g + neighbor.h; } } for (let i = 0; i < rows; i++) { for (let j = 0; j < cols; j++) { grid[i][j].show(); } } start.show('yellow'); end.show('purple'); for (let i = 0; i < open.length; i++) { open[i].show('green'); } for (let i = 0; i < closed.length; i++) { closed[i].show('red'); } }
关键说明
- 给Cell绑定行列索引是获取邻居的核心前提,通过二维数组索引就能快速定位相邻单元格
getNeighbors方法通过方向偏移量+边界检查,确保只获取有效邻居- 完善后的update函数实现了A*算法的基础流程,你可以继续扩展障碍物、对角线移动等功能
内容的提问来源于stack exchange,提问作者borbzo
相关产品推荐
相关产品推荐

