javascript 避障实践
1. 核心避障逻辑:先“感知”再“行动”
避障的本质是两步走,缺一不可:
1. 碰撞检测(感知):这是避障的前提,需根据物体形态选择合适检测方式,避免过度消耗性能。
- 简单场景(如矩形元素):用 getBoundingClientRect() 获取元素坐标,通过对比x、y轴范围判断是否重叠,性能最优。
- 复杂场景(如不规则图形):使用Canvas的 isPointInPath() 或第三方库(如 matter.js ),但需注意控制检测频率(如每16ms检测1次,匹配屏幕刷新率)。
2. 路径修正(行动):检测到碰撞后,避免“随机偏移”,优先选择成本最低的规避方式。
- 优先沿空白区域最大的方向移动(如检测上下左右四个方向的可移动距离,选最大的)。
- 复杂路径(如多障碍物):引入简化版A*算法,用网格划分区域,只计算“可通行网格”的最短路径,减少计算量。
2. 关键最佳实践
-降低检测复杂度:避免对所有元素实时检测,可给障碍物加“碰撞标签”,只检测目标物体与标签元素的交互。
- 加入“缓冲距离”:检测时不等到完全重叠才触发避障,预留5-10px缓冲,避免视觉上的“擦边碰撞”,提升体验。
- 性能兜底:在低配置设备或元素过多时,通过 requestAnimationFrame() 控制避障逻辑的执行频率,防止页面卡顿。
- 优先复用成熟库:复杂场景(如游戏、3D模型避障)直接使用 Three.js (3D)、 p5.js (2D)等库,内置的避障API已做过性能优化,避免重复造轮子。
以下是基于矩形碰撞检测的JS避障demo,包含HTML结构与完整JS逻辑,可直接复制运行,核心实现“元素移动→碰撞检测→自动偏移”的闭环:
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<title>JS矩形避障Demo</title>
<style>
#container {
width: 600px;
height: 400px;
border: 2px solid #333;
position: relative;
margin: 20px auto;
}
.mover { /* 可移动元素(需要避障的物体) */
width: 30px;
height: 30px;
background: #4285F4;
border-radius: 50%;
position: absolute;
top: 10px;
left: 10px;
}
.obstacle { /* 障碍物 */
width: 80px;
height: 80px;
background: #EA4335;
position: absolute;
}
</style>
</head>
<body>
<div id="container">
<div class="mover" id="mover"></div>
<div class="obstacle" style="top: 100px; left: 200px;"></div>
<div class="obstacle" style="top: 250px; left: 400px;"></div>
</div>
<script>
// 1. 获取核心元素
const container = document.getElementById('container');
const mover = document.getElementById('mover');
const obstacles = document.querySelectorAll('.obstacle'); // 所有障碍物
// 2. 移动配置(速度、方向)
let speed = 2;
let dir = { x: 1, y: 1 }; // 初始向右下方移动
// 3. 核心:矩形碰撞检测函数
function isColliding(elem1, elem2) {
const rect1 = elem1.getBoundingClientRect();
const rect2 = elem2.getBoundingClientRect();
// 检测两个矩形是否重叠(不重叠的4种情况取反)
return !(
rect1.right < rect2.left ||
rect1.left > rect2.right ||
rect1.bottom < rect2.top ||
rect1.top > rect2.bottom
);
}
// 4. 避障与移动逻辑
function moveWithAvoidance() {
// 获取当前移动元素位置
let currentTop = parseInt(mover.style.top) || 0;
let currentLeft = parseInt(mover.style.left) || 0;
// 先假设按原方向移动,计算新位置
let newTop = currentTop + dir.y * speed;
let newLeft = currentLeft + dir.x * speed;
// 检测边界碰撞(碰到容器边缘反弹)
if (newTop <= 0 || newTop + mover.offsetHeight >= container.offsetHeight) {
dir.y *= -1;
newTop = currentTop + dir.y * speed;
}
if (newLeft <= 0 || newLeft + mover.offsetWidth >= container.offsetWidth) {
dir.x *= -1;
newLeft = currentLeft + dir.x * speed;
}
// 检测与障碍物碰撞:碰撞则切换方向(简化版避障,优先上下偏移)
let hasCollision = false;
obstacles.forEach(obs => {
// 临时更新位置检测碰撞(不实际移动)
mover.style.top = `${newTop}px`;
mover.style.left = `${newLeft}px`;
if (isColliding(mover, obs)) {
hasCollision = true;
dir.y *= -1; // 碰撞时先上下反弹,避免卡墙
newTop = currentTop + dir.y * speed;
}
});
// 应用最终位置
mover.style.top = `${newTop}px`;
mover.style.left = `${newLeft}px`;
// 循环执行(匹配屏幕刷新率,避免卡顿)
requestAnimationFrame(moveWithAvoidance);
}
// 5. 启动避障移动
moveWithAvoidance();
</script>
</body>
</html>
核心逻辑说明
1. 碰撞检测:通过 getBoundingClientRect() 获取元素坐标,用“矩形不重叠的4种情况取反”判断是否碰撞,性能极优。
2. 避障策略:碰到障碍物时优先切换上下方向( dir.y *= -1 ),同时保留边界反弹逻辑,避免元素“卡死”。
3. 性能优化:用 requestAnimationFrame() 替代 setInterval() ,确保动画帧率与屏幕同步(约60帧/秒),无卡顿感。
更多推荐
所有评论(0)