Fabric.js相邻矩形相交检测误判问题咨询及解决方法
哈哈,这个问题我当初用Fabric.js做排版工具时踩过一模一样的坑!咱们来拆解原因和解决办法:
1. 默认碰撞检测的“边界接触即相交”规则
Fabric.js内置的intersects()方法(以及它底层的碰撞检测逻辑),默认是把两个元素的边缘刚好贴合的情况判定为相交的。也就是说,当第一个矩形右边界是0+50=50,第二个矩形左边界刚好是50时,两者边缘接触,就会返回true。
2. 浮点精度的隐形干扰
Canvas渲染和Fabric的坐标计算偶尔会有微小的浮点误差——比如你设置的left:50,实际计算时可能是50.0000001或者49.9999999,这种极细微的偏移会让本来应该对齐的矩形出现“伪重叠”,也会触发相交判定。
另外还有个容易忽略的点:如果你的矩形设置了stroke和strokeWidth,Fabric计算元素边界时会把描边宽度算进去!比如1px描边会让矩形实际宽度增加2px(左右各1px),这时候x0宽50的矩形右边界会变成51,第二个矩形放x50自然就重叠了。
根据你的需求,推荐这几种方案:
方案1:自定义严格的碰撞检测函数
完全绕过Fabric的默认逻辑,自己写一个只判定“真正重叠”的函数,边缘接触不算相交:
function isStrictlyOverlapping(rectA, rectB) { // 获取两个矩形的精确边界 const aLeft = rectA.left; const aRight = rectA.left + rectA.width; const aTop = rectA.top; const aBottom = rectA.top + rectA.height; const bLeft = rectB.left; const bRight = rectB.left + rectB.width; const bTop = rectB.top; const bBottom = rectB.top + rectB.height; // 核心逻辑:只有当一个矩形的内部和另一个重叠时才返回true return !(aRight <= bLeft || aLeft >= bRight || aBottom <= bTop || aTop >= bBottom); }
用这个函数检测你那两个刚好对齐的矩形,会返回false,完美符合预期。
方案2:给默认检测加容差阈值
如果不想重写逻辑,可以给边界判断加一个极小的容差,抵消浮点误差和边界接触的情况:
function intersectsWithTolerance(rectA, rectB, tolerance = 0.1) { const aBounds = rectA.getBoundingRect(); const bBounds = rectB.getBoundingRect(); return !( aBounds.right - tolerance <= bBounds.left || aBounds.left + tolerance >= bBounds.right || aBounds.bottom - tolerance <= bBounds.top || aBounds.top + tolerance >= bBounds.bottom ); }
这个函数里,只要两个元素的间隙小于tolerance(默认0.1px)才会判定为相交,边缘接触的情况会被排除。
方案3:排查描边干扰
如果你的矩形有描边,记得把strokeWidth设为0,或者计算位置时把描边宽度考虑进去。比如有1px描边的矩形,第二个矩形的左边界应该设为50 + 2*1 = 52才不会重叠。
用你的代码改一下验证:
const canvas = new fabric.Canvas('canvas'); const rect1 = new fabric.Rect({ left: 0, top: 0, fill: 'black', width: 50, height: 50, strokeWidth: 0 // 确保无描边干扰 }); const rect2 = new fabric.Rect({ left: 50, top: 0, fill: 'red', width: 50, height: 50, strokeWidth: 0 }); canvas.add(rect1, rect2); // 用自定义函数检测 console.log(isStrictlyOverlapping(rect1, rect2)); // 输出 false,符合预期
内容的提问来源于stack exchange,提问作者haggis

