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

如何使用JavaScript判断矩形与旋转矩形是否相交

解决旋转矩形的SVG碰撞检测问题

嘿,我来帮你搞定这个旋转矩形的碰撞检测需求!你找到的那段用getBoundingClientRect()的代码有个关键问题:它返回的是轴对齐的包围盒(AABB)——不管你的矩形怎么旋转,这个方法都会生成一个和屏幕坐标轴平行的矩形框。用它来检测旋转后的矩形碰撞,会出现很多误判(比如两个旋转后的矩形实际没接触,但它们的AABB重叠了),完全满足不了你的需求。

正确的解决方案是用分离轴定理(Separating Axis Theorem, SAT),这是检测凸多边形碰撞的黄金标准,而矩形刚好属于凸多边形,完美适配。下面是具体的实现步骤和代码:

步骤1:获取矩形的实际顶点坐标

首先需要一个工具函数,用来获取SVG矩形在经过CSS/SVG变换后的四个顶点的真实坐标:

function getPolygonVertices(element) {
  // 获取元素的变换矩阵(包含旋转、平移、缩放等所有变换)
  const transform = element.getCTM();
  // 获取矩形的基础边界(未变换的原始尺寸)
  const rect = element.getBBox();
  const x = rect.x;
  const y = rect.y;
  const width = rect.width;
  const height = rect.height;
  
  // 定义矩形的四个原始顶点
  const rawVertices = [
    { x: x, y: y },
    { x: x + width, y: y },
    { x: x + width, y: y + height },
    { x: x, y: y + height }
  ];
  
  // 应用变换矩阵,计算出实际显示的顶点坐标
  return rawVertices.map(vertex => {
    return {
      x: vertex.x * transform.a + vertex.y * transform.c + transform.e,
      y: vertex.x * transform.b + vertex.y * transform.d + transform.f
    };
  });
}

步骤2:实现分离轴定理核心逻辑

接下来实现SAT的核心函数,用来判断两个凸多边形(这里就是两个矩形的顶点集合)是否碰撞:

// 主函数:判断两个多边形是否碰撞
function doPolygonsIntersect(poly1, poly2) {
  // 获取两个多边形所有边的法线(这些是候选的分离轴)
  const axes = [...getAxes(poly1), ...getAxes(poly2)];
  
  // 遍历每个轴,检查是否能分离两个多边形
  for (const axis of axes) {
    const [min1, max1] = projectPolygon(poly1, axis);
    const [min2, max2] = projectPolygon(poly2, axis);
    
    // 如果投影区间不重叠,说明存在分离轴,没有碰撞
    if (max1 < min2 || max2 < min1) {
      return false;
    }
  }
  
  // 所有轴都无法分离,说明两个多边形碰撞
  return true;
}

// 获取多边形所有边的法线轴
function getAxes(polygon) {
  const axes = [];
  const vertexCount = polygon.length;
  
  for (let i = 0; i < vertexCount; i++) {
    const currentVertex = polygon[i];
    const nextVertex = polygon[(i + 1) % vertexCount];
    // 计算边的向量
    const edge = { x: nextVertex.x - currentVertex.x, y: nextVertex.y - currentVertex.y };
    // 生成垂直于边的法线(分离轴)
    const normal = { x: -edge.y, y: edge.x };
    // 归一化法线,避免数值精度问题
    axes.push(normalizeVector(normal));
  }
  
  return axes;
}

// 将多边形投影到指定轴上,返回投影的最小和最大值
function projectPolygon(polygon, axis) {
  let minProjection = dotProduct(polygon[0], axis);
  let maxProjection = minProjection;
  
  for (const vertex of polygon) {
    const projection = dotProduct(vertex, axis);
    if (projection < minProjection) minProjection = projection;
    if (projection > maxProjection) maxProjection = projection;
  }
  
  return [minProjection, maxProjection];
}

// 计算向量点积
function dotProduct(v1, v2) {
  return v1.x * v2.x + v1.y * v2.y;
}

// 归一化向量
function normalizeVector(v) {
  const length = Math.sqrt(v.x * v.x + v.y * v.y);
  return { x: v.x / length, y: v.y / length };
}

步骤3:组合成最终的碰撞检测函数

把上面的工具函数和核心逻辑组合起来,得到你需要的最终函数:

function checkRectCollision(svgRect1, svgRect2) {
  const rect1Vertices = getPolygonVertices(svgRect1);
  const rect2Vertices = getPolygonVertices(svgRect2);
  return doPolygonsIntersect(rect1Vertices, rect2Vertices);
}

使用方法

假设你有两个SVG矩形元素,比如:

<rect id="rect1" x="50" y="50" width="100" height="50" transform="rotate(30 100 75)" />
<rect id="rect2" x="150" y="80" width="80" height="60" />

你可以这样调用检测:

const rect1 = document.getElementById('rect1');
const rect2 = document.getElementById('rect2');
const isColliding = checkRectCollision(rect1, rect2);
console.log(isColliding); // 输出true或false

这个方法的优势在于:它完全考虑了元素的旋转、平移、缩放等所有变换,检测精度极高,而且不仅适用于矩形,任何SVG凸多边形(比如<polygon>、<path>)都可以用这个逻辑来检测碰撞。

内容的提问来源于stack exchange,提问作者Jack Buckley

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.05.20 12:32:56