You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.09 17:12:36