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

如何检测旋转后的div元素是否与另一个div元素发生重叠?

旋转DIV的碰撞检测问题

你的碰撞检测函数采用的是**轴对齐包围盒(AABB)**检测逻辑,它只能正确判断未旋转的矩形是否重叠——因为它计算的是元素的外接轴对齐矩形范围,当元素旋转后,实际的可视区域和这个外接矩形并不重合,哪怕元素本身没和另一个DIV重叠,外接矩形也可能产生交集,导致函数误返回true。

解决方案:使用分离轴定理(SAT)

要正确检测旋转矩形的碰撞,需要用到分离轴定理(Separating Axis Theorem),它能处理任意凸多边形的碰撞判断,旋转后的矩形属于凸多边形范畴。

以下是实现代码:

// 获取元素的四个顶点坐标(考虑旋转)
function getElementVertices($el) {
  var offset = $el.offset();
  var width = $el.outerWidth(true);
  var height = $el.outerHeight(true);
  var transform = $el.css('transform');
  
  // 解析旋转角度(仅处理2D旋转)
  var angle = 0;
  if (transform !== 'none') {
    var matrix = transform.match(/matrix\(([^)]+)\)/)[1].split(',');
    angle = Math.atan2(parseFloat(matrix[1]), parseFloat(matrix[0])) * (180 / Math.PI);
  }
  
  var centerX = offset.left + width / 2;
  var centerY = offset.top + height / 2;
  var halfWidth = width / 2;
  var halfHeight = height / 2;
  
  // 计算四个顶点相对于中心的坐标
  var points = [
    {x: -halfWidth, y: -halfHeight},
    {x: halfWidth, y: -halfHeight},
    {x: halfWidth, y: halfHeight},
    {x: -halfWidth, y: halfHeight}
  ];
  
  // 将顶点旋转指定角度后转换为绝对坐标
  var rad = angle * Math.PI / 180;
  var cos = Math.cos(rad);
  var sin = Math.sin(rad);
  
  return points.map(point => {
    return {
      x: centerX + (point.x * cos - point.y * sin),
      y: centerY + (point.x * sin + point.y * cos)
    };
  });
}

// 基于分离轴定理的碰撞检测
function collision($div1, $div2) {
  var vertices1 = getElementVertices($div1);
  var vertices2 = getElementVertices($div2);
  
  // 获取所有需要检测的分离轴(两个矩形的边的垂直方向)
  function getAxes(vertices) {
    var axes = [];
    for (var i = 0; i < vertices.length; i++) {
      var p1 = vertices[i];
      var p2 = vertices[(i + 1) % vertices.length];
      // 计算边的垂直向量(分离轴)
      var axis = {
        x: p2.y - p1.y,
        y: p1.x - p2.x
      };
      // 归一化轴向量
      var length = Math.sqrt(axis.x * axis.x + axis.y * axis.y);
      axes.push({
        x: axis.x / length,
        y: axis.y / length
      });
    }
    return axes;
  }
  
  var axes = getAxes(vertices1).concat(getAxes(vertices2));
  
  // 检测每个轴上的投影是否分离
  for (var i = 0; i < axes.length; i++) {
    var axis = axes[i];
    
    // 计算第一个矩形在轴上的投影范围
    var min1 = Infinity, max1 = -Infinity;
    for (var j = 0; j < vertices1.length; j++) {
      var projection = vertices1[j].x * axis.x + vertices1[j].y * axis.y;
      min1 = Math.min(min1, projection);
      max1 = Math.max(max1, projection);
    }
    
    // 计算第二个矩形在轴上的投影范围
    var min2 = Infinity, max2 = -Infinity;
    for (var k = 0; k < vertices2.length; k++) {
      var projection = vertices2[k].x * axis.x + vertices2[k].y * axis.y;
      min2 = Math.min(min2, projection);
      max2 = Math.max(max2, projection);
    }
    
    // 如果投影不重叠,则两个矩形不碰撞
    if (max1 < min2 || max2 < min1) {
      return false;
    }
  }
  
  // 所有轴上的投影都重叠,说明碰撞
  return true;
}

说明

  1. getElementVertices函数负责计算旋转后元素的四个绝对顶点坐标,通过解析元素的transform属性获取旋转角度,再对顶点进行旋转变换。
  2. collision函数基于分离轴定理,先提取两个矩形所有边的垂直方向作为分离轴,再检测两个矩形在每个轴上的投影是否重叠——只要有一个轴上投影不重叠,就说明没有碰撞;所有轴投影都重叠则判定为碰撞。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.18 15:15:30