如何检测旋转后的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; }
说明
getElementVertices函数负责计算旋转后元素的四个绝对顶点坐标,通过解析元素的transform属性获取旋转角度,再对顶点进行旋转变换。collision函数基于分离轴定理,先提取两个矩形所有边的垂直方向作为分离轴,再检测两个矩形在每个轴上的投影是否重叠——只要有一个轴上投影不重叠,就说明没有碰撞;所有轴投影都重叠则判定为碰撞。
内容的提问来源于stack exchange,提问作者user3731362
相关产品推荐
相关产品推荐

