Canvas(JavaScript)单元格重置后尺寸变大问题及BFS网格优化咨询
Hey there! Let's tackle your two issues one by one—first the cell resizing bug, then some polished approaches for BFS algorithm demos.
Fixing the Reset Cell Size Bug
The root cause of your oversized cell issue is a coordinate mismatch in the resetCell method. Your Cell class stores this.x and this.y as grid indices (like 0 to 59), not pixel positions on the canvas. When you called clearRect(this.x, this.y, this.size, this.size), you were clearing a tiny area starting at the grid index value (e.g., x=1 instead of x=1*20=20 pixels), leaving old stroke/fill data that overlapped with the redrawn cell.
Here's the fixed resetCell method:
this.resetCell = function () { // Use pixel coordinates (grid index * cell size) instead of grid indices const pixelX = this.x * this.size; const pixelY = this.y * this.size; c.clearRect(pixelX, pixelY, this.size, this.size); this.update(); }
This ensures you fully clear the exact area of the cell before redrawing it, eliminating the overlapping visual glitch.
Better Grid & Control Schemes for BFS Demos
For a more intuitive and educational BFS demo, here are some optimized practices:
1. Use ES6 Classes for Cleaner, Maintainable Code
Replace the prototype-based Cell function with a class, and add state properties to track BFS progress (unvisited, visited, start, end, path):
class Cell { constructor(x, y, size) { this.x = x; this.y = y; this.size = size; this.state = 'unvisited'; // Tracks BFS status } draw() { c.beginPath(); const pixelX = this.x * this.size; const pixelY = this.y * this.size; c.rect(pixelX, pixelY, this.size, this.size); c.strokeStyle = 'black'; c.stroke(); // Set color based on state for clear visualization switch(this.state) { case 'start': c.fillStyle = '#2ecc71'; break; // Green case 'end': c.fillStyle = '#e74c3c'; break; // Red case 'visited': c.fillStyle = '#3498db'; break; // Blue case 'path': c.fillStyle = '#f1c40f'; break; // Yellow default: c.fillStyle = 'white'; } c.fill(); c.closePath(); } reset() { const pixelX = this.x * this.size; const pixelY = this.y * this.size; c.clearRect(pixelX, pixelY, this.size, this.size); this.state = 'unvisited'; this.draw(); } }
2. Add Interactive Setup for BFS
Let users define the start and end points with clicks, which makes the demo more engaging:
let startCell = null; let endCell = null; canvas.addEventListener('click', (e) => { const rect = canvas.getBoundingClientRect(); const pixelX = e.clientX - rect.left; const pixelY = e.clientY - rect.top; // Convert pixel position to grid indices const gridX = Math.floor(pixelX / grid.size); const gridY = Math.floor(pixelY / grid.size); // Toggle start/end cells on click if (!startCell) { startCell = cells[gridX][gridY]; startCell.state = 'start'; } else if (!endCell && cells[gridX][gridY] !== startCell) { endCell = cells[gridX][gridY]; endCell.state = 'end'; } cells[gridX][gridY].draw(); });
3. Visualize BFS Step-by-Step
Instead of running BFS instantly, add delays to show the queue progression and node visiting order—this is key for teaching the algorithm:
async function runBFS(start, end) { const queue = [start]; start.state = 'visited'; const parentMap = new Map(); // Tracks path back to start while (queue.length > 0) { await new Promise(resolve => setTimeout(resolve, 60)); // 60ms delay per step const current = queue.shift(); // Reconstruct path if end is found if (current === end) { let pathNode = end; while (pathNode) { pathNode.state = 'path'; pathNode.draw(); pathNode = parentMap.get(pathNode); } break; } // Get valid adjacent neighbors (no out-of-bounds) const neighbors = getValidNeighbors(current); for (const neighbor of neighbors) { if (neighbor.state === 'unvisited') { neighbor.state = 'visited'; neighbor.draw(); parentMap.set(neighbor, current); queue.push(neighbor); } } } } function getValidNeighbors(cell) { const neighbors = []; if (cell.x > 0) neighbors.push(cells[cell.x - 1][cell.y]); if (cell.x < grid.x - 1) neighbors.push(cells[cell.x + 1][cell.y]); if (cell.y > 0) neighbors.push(cells[cell.x][cell.y - 1]); if (cell.y < grid.y - 1) neighbors.push(cells[cell.x][cell.y + 1]); return neighbors; }
4. Add Demo Controls
Add simple buttons to reset the grid and trigger BFS, so users can repeat the demo easily:
<!-- Add this to your HTML content --> <div class="controls"> <button id="resetBtn">Reset Grid</button> <button id="runBtn">Run BFS</button> </div>
// Bind button events in your JS document.getElementById('resetBtn').addEventListener('click', () => { initCells(); startCell = null; endCell = null; }); document.getElementById('runBtn').addEventListener('click', () => { if (startCell && endCell) runBFS(startCell, endCell); });
内容的提问来源于stack exchange,提问作者Luka Gabadze

