源码在效果图后面
可标记 障碍 起始点 终点
点击寻路按钮后,表格上会自动出现一条蓝色最佳路径(加了一格一格显示的动画)
以下是效果图 橙色起点 绿色终点 红色障碍物
以下是寻路结果
源代码
<!DOCTYPE html>
<html lang="zh">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=0.9">
<title>广度优先搜索算法</title>
<style>
body {
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
height: 100vh;
margin: 0;
background-color: #f0f0f0;
}
#grid {
display: grid;
grid-template-columns: repeat(8, 50px);
grid-template-rows: repeat(8, 50px);
gap: 1px;
background-color: gray;
}
.cell {
width: 50px;
height: 50px;
background-color: white;
border: 1px solid #ccc;
cursor: pointer;
}
.cell.obstacle {
background-color: red;
}
.cell.start {
background-color: orange;
}
.cell.end {
background-color: green;
}
.cell.visited {
background-color: lightpurple;
}
.cell.path {
background-color: blue;
}
#buttons {
margin: 20px 0;
}
button {
padding: 10px 15px;
margin: 0 5px;
border: none;
border-radius: 5px;
box-shadow: 0 2px 5px rgba(0, 0, 0, 0.2);
cursor: pointer;
transition: background-color 0.3s;
}
button:hover {
background-color: #ddd;
}
#markObstacle {
background-color: red;
color: white;
}
#reset {
background-color: green;
color: white;
}
#markStart {
background-color: orange;
color: white;
}
#markEnd {
background-color: blue;
color: white;
}
#startSearch {
background-color: purple;
color: white;
}
</style>
</head>
<body>
<div id="grid"></div>
<div id="buttons">
<button id="markObstacle">标记障碍物</button>
<button id="reset">重置</button>
<button id="markStart">标记起点</button>
<button id="markEnd">标记终点</button>
<button id="startSearch">开始寻路</button>
</div>
<script>
const OBSTACLE = 1;
const START = 2;
const END = 3;
const VISITED = 4;
const PATH = 5;
let grid = Array.from({ length: 8 }, () => Array(8).fill(0));
let startCell = null;
let endCell = null;
const gridElement = document.getElementById('grid');
// 创建格子
for (let i = 0; i < 8; i++) {
for (let j = 0; j < 8; j++) {
const cell = document.createElement('div');
cell.classList.add('cell');
cell.dataset.row = i;
cell.dataset.col = j;
cell.addEventListener('click', () => handleCellClick(i, j));
gridElement.appendChild(cell);
}
}
function handleCellClick(row, col) {
const cell = gridElement.children[row * 8 + col];
if (cell.classList.contains('obstacle')) {
cell.classList.remove('obstacle');
grid[row][col] = 0;
} else if (cell.classList.contains('start')) {
alert('起点已存在,请先重置或选择其他格子');
} else if (cell.classList.contains('end')) {
alert('终点已存在,请先重置或选择其他格子');
} else if (cell.classList.contains('visited') || cell.classList.contains('path')) {
alert('此格子已被访问或是路径');
} else {
if (markingMode === 'obstacle') {
cell.classList.add('obstacle');
grid[row][col] = OBSTACLE;
} else if (markingMode === 'start') {
if (startCell) {
grid[startCell.row][startCell.col] = 0;
gridElement.children[startCell.row * 8 + startCell.col].classList.remove('start');
}
cell.classList.add('start');
grid[row][col] = START;
startCell = { row, col };
} else if (markingMode === 'end') {
if (endCell) {
grid[endCell.row][endCell.col] = 0;
gridElement.children[endCell.row * 8 + endCell.col].classList.remove('end');
}
cell.classList.add('end');
grid[row][col] = END;
endCell = { row, col };
}
}
}
let markingMode = '';
document.getElementById('markObstacle').onclick = () => markingMode = 'obstacle';
document.getElementById('markStart').onclick = () => markingMode = 'start';
document.getElementById('markEnd').onclick = () => markingMode = 'end';
document.getElementById('reset').onclick = resetGrid;
document.getElementById('startSearch').onclick = startSearch;
function resetGrid() {
grid = Array.from({ length: 8 }, () => Array(8).fill(0));
startCell = null;
endCell = null;
Array.from(gridElement.children).forEach(cell => {
cell.className = 'cell';
});
}
function startSearch() {
if (!startCell || !endCell) {
alert('请标记起点和终点');
return;
}
const queue = [startCell];
const visited = new Set();
visited.add(`${startCell.row},${startCell.col}`);
const parentMap = {};
while (queue.length > 0) {
const current = queue.shift();
const { row, col } = current;
if (row === endCell.row && col === endCell.col) {
reconstructPath(parentMap, current);
return;
}
for (const [dx, dy] of [[-1, 0], [1, 0], [0, -1], [0, 1]]) {
const newRow = row + dx;
const newCol = col + dy;
if (newRow >= 0 && newRow < 8 && newCol >= 0 && newCol < 8 &&
grid[newRow][newCol] !== OBSTACLE && !visited.has(`${newRow},${newCol}`)) {
queue.push({ row: newRow, col: newCol });
visited.add(`${newRow},${newCol}`);
parentMap[`${newRow},${newCol}`] = current;
gridElement.children[newRow * 8 + newCol].classList.add('visited');
}
}
}
alert('未找到路径');
}
function reconstructPath(parentMap, end) {
let current = end;
const path = [];
while (current) {
path.push(current);
current = parentMap[`${current.row},${current.col}`];
}
// 反转路径数组
path.reverse();
// 逐个显示路径
path.forEach((cell, index) => {
setTimeout(() => {
const { row, col } = cell;
gridElement.children[row * 8 + col].classList.add('path');
}, index * 100); // 每个格子延迟100毫秒
});
}
</script>
</body>
</html>