如何基于向量与角度计算视口边缘交点?附演示参考
从矩形约束点延伸到视口边缘的最近交点实现方案
嘿,看你已经搞定了鼠标点被蓝色方块边缘约束的逻辑(就是演示里那个跟着鼠标走但不会跑出方块的红点),现在想基于这个,从方块中心画一条穿过红点的直线,找到这条直线和视口边缘的最近交点对吧?我来给你一步步拆解怎么实现:
第一步:先把已知参数理清楚
首先你得确保手里有这些变量(应该都是你现有代码里已经能拿到的):
- 蓝色方块的中心坐标:
center = { x: 方块中心X, y: 方块中心Y } - 约束后的红点坐标(也就是鼠标对应到方块边缘/内部的点):
clampedPoint = { x: 红点X, y: 红点Y } - 当前视口的尺寸:
viewport = { width: window.innerWidth, height: window.innerHeight }
第二步:算出直线的方向向量
我们需要从方块中心指向红点的方向,把这个向量转成单位向量(长度为1的向量),这样后续计算交点会更方便:
// 计算中心到红点的方向向量 const dirX = clampedPoint.x - center.x; const dirY = clampedPoint.y - center.y; // 计算向量长度,防止除以0(如果红点和中心重合,比如鼠标在方块正中心,就默认长度为1) const vectorLength = Math.sqrt(dirX * dirX + dirY * dirY) || 1; // 转成单位向量 const unitDirX = dirX / vectorLength; const unitDirY = dirY / vectorLength;
第三步:计算直线与视口四边的交点
直线可以用参数方程表示:x = center.x + t * unitDirX,y = center.y + t * unitDirY,这里的t是正数(因为我们要往红点的方向延伸,远离方块中心)。我们要算出直线和视口四个边相交时的t值,然后取最小的正t对应的交点——这个就是离方块中心最近的视口边缘点。
代码实现如下:
let minT = Infinity; // 用来存最小的有效t值 let nearestEdgePoint = { x: 0, y: 0 }; // 最终的最近边缘点 // 检查与左边缘(x=0)的交点 if (unitDirX !== 0) { const t = (0 - center.x) / unitDirX; if (t > 0) { // 只考虑往红点方向延伸的交点 const intersectY = center.y + t * unitDirY; // 确保交点在视口的上下范围内 if (intersectY >= 0 && intersectY <= viewport.height) { if (t < minT) { minT = t; nearestEdgePoint = { x: 0, y: intersectY }; } } } } // 检查与右边缘(x=viewport.width)的交点 if (unitDirX !== 0) { const t = (viewport.width - center.x) / unitDirX; if (t > 0) { const intersectY = center.y + t * unitDirY; if (intersectY >= 0 && intersectY <= viewport.height) { if (t < minT) { minT = t; nearestEdgePoint = { x: viewport.width, y: intersectY }; } } } } // 检查与上边缘(y=0)的交点 if (unitDirY !== 0) { const t = (0 - center.y) / unitDirY; if (t > 0) { const intersectX = center.x + t * unitDirX; if (intersectX >= 0 && intersectX <= viewport.width) { if (t < minT) { minT = t; nearestEdgePoint = { x: intersectX, y: 0 }; } } } } // 检查与下边缘(y=viewport.height)的交点 if (unitDirY !== 0) { const t = (viewport.height - center.y) / unitDirY; if (t > 0) { const intersectX = center.x + t * unitDirX; if (intersectX >= 0 && intersectX <= viewport.width) { if (t < minT) { minT = t; nearestEdgePoint = { x: intersectX, y: viewport.height }; } } } }
第四步:处理边界情况
这里有个需要注意的点:如果红点和方块中心完全重合(也就是鼠标在方块内部的正中心),我们默认了向量长度为1,这时候代码会随机找到一个视口边缘点。你可以根据自己的需求调整这个逻辑——比如改成指向鼠标原本的位置方向,而不是默认的方向。
把这段代码和你现有的约束红点的逻辑结合起来,就能得到你想要的从方块中心穿过红点,直达视口最近边缘的点啦!
内容的提问来源于stack exchange,提问作者Jake Cattrall
相关产品推荐
相关产品推荐

