最近在做WEB前端项目时,需要识别一个元素是否有某些部位出现在可视区域内,当有某个部位出现在可视区域时,就执行该元素绑定的动画,如果完全不在可视区域内则移除其动画,当再次出现时重复执行动画。
众所周知,元素是以一个矩形的盒模型的形式呈现在网页中,而且浏览器的可视区域也是一个矩形,那么这个需求就变成了某个元素的盒模型(矩形B)是否有某个部分出现在浏览器可视区域(矩形A)中,如果有则执行动画。
将需求提炼一下,问题为:判断矩形APa1(Xa1,Ya1), Pa2(Xa2,Ya2)与矩形BPb1(Xb1,Yb1), Pb2(Xb2,Yb2)是否相交。
最初的思路如下:
Pa1
与Pb1
距离原点(0,0)
更远的点(即x轴方向与y轴方向坐标值较大的点),将其标记为M
(图1的中粉色点);Pa2
与Pb2
距离原点更近的点(即x轴方向与y轴方向坐标值较小的点),将其标记为N
(图1中的橙色点);M
的x轴坐标值和y轴坐标值均比点N
的x轴坐标和y轴坐标小(即,M点和N点可以构成一个新的矩形),则两个矩形相交,否则不相交。方法出来了,总要经过验证才知道是否正确,那么接下来就对以下几种情况进行验证:
几种相交的情况:
再举两个不相交的情况:
如上所示,除了图1、图2通过上述方法得出正确答案外,其他情况均无法得出正确答案,由此可见上述方法是错误的。
那么如何才能得到正确的方法呢?仔细观察上面列出的几种情况后,想到了一个新的思路:如果两个矩形相交,那么矩形A的中心点Pa3(Xa3,Ya3)
与矩形B的中心点Pb3(Xb3,Yb3)
在x轴方向上的距离和y轴方向的距离一定满足以下条件:
Pa3
和Pb3
的距离一定小于或等于矩形A的宽度+矩形B的宽度的一半;Pa3
和Pb3
的距离一定小于或等于矩形A的高度+矩形B的高度的一半;只要满足以上两个条件,那么就可以判定为两个矩形相交。
矩形A
宽:Wa = Xa2 - Xa1
高:Ha = Ya2 - Ya1
中心坐标:[Xa3, Ya3] = [(Xa2 + Xa1) / 2, (Ya2 + Ya1) / 2]
矩形B
宽:Wb = Xb2 - Xb1
高:Hb = Yb2 - Yb1
中心坐标:[Xb3, Yb3] = [(Xb2 + Xb1) / 2, (Yb2 + Yb1) / 2]
上述两个条件即可表示为
1) |Xb3 - Xa3| <= Wa/2 + Wb/2
2) |Yb3 - Ya3| <= Ha/2 + Hb/2
再代入宽、高、中心点坐标计算公式并简化,即为:
1) |Xb2 + Xb1 - Xa2 - Xa1| <= Xa2 - Xa1 + Xb2 - Xb1
2) |Yb2 + Yb1 - Ya2 - Ya1| <= Ya2 - Ya1 + Yb2 - Yb1
如果想要得到相交区域的新矩形,那么需要取得相交区域的左上角顶点与右下角坐标,有方法如下:
设相交区域的新矩形为c[(Xc1,Yc1), (Xc2,Yc2)]
Xc1 = max(Xa1,Xb1)
Yc1 = max(Ya1,Yb1)
Xc2 = min(Xa2,Xb2)
Yc2 = min(Xa2,Xb2)
也可以通过判断上述获取新矩形的方法来判定两个矩形是否相交,方法如下: 若同时满足以下两个条件,则可以判定两个矩形相交。 1) Xc1 <= Xc2 2) Yc1 <= Yc2 即: max(Xa1,Xb1) <= min(Xa2,Xb2) max(Ya1,Yb1) <= min(Ya2,Yb2)
根据以上结论,有如下javascript代码提供参考:
/*
* rect obj {x1:num, y1:num, x2:num, y2:num} 定义矩形的两个顶点的坐标集合
*/
var isIntersection = function(rectA, rectB){
let lftp = [Math.max(rectA.x1, rectB.x1), Math.max(rectA.y1, rectB.y1)],
rgbt = [Math.min(rectA.x2, rectB.x2), Math.min(rectA.y2, rectB.y2)];
if(lftp[0] <= rgbt[0] && lftp[1] <= rgbt[1]){
return true;
}
return false;
}
var rectA = {x1: 6, y1: 6, x2: 14, y2: 12},
rectB = {x1: 10, y1: 10, x2: 20, y2: 16},
rectC = {x1: 8, y1: 8, x2: 12, y2: 10},
rectD = {x1: 4, y1: 8, x2: 16, y2: 10},
rectE = {x1: 8, y1: 8, x2: 12, y2: 16},
rectF = {x1: 10, y1: 14, x2: 16, y2: 18},
rectG = {x1: 16, y1: 10, x2: 20, y2: 18};
console.log(isIntersection(rectA, rectB)); // true
console.log(isIntersection(rectA, rectC)); // true
console.log(isIntersection(rectA, rectD)); // true
console.log(isIntersection(rectA, rectE)); // true
console.log(isIntersection(rectA, rectF)); // false
console.log(isIntersection(rectA, rectG)); // false
特别注意:以上结论若要成立,还有一个**先决条件**,即:两个矩形均为与轴对齐的矩形,否则将不适用,例如矩形B被旋转后,如图8、图9:
本文采用 「CC BY-NC-SA 4.0」创作共享协议,转载请标注以下信息:
原文出处:Yiiven https://cloud.tencent.com/developer/article/2193257
扫码关注腾讯云开发者
领取腾讯云代金券
Copyright © 2013 - 2025 Tencent Cloud. All Rights Reserved. 腾讯云 版权所有
深圳市腾讯计算机系统有限公司 ICP备案/许可证号:粤B2-20090059 深公网安备号 44030502008569
腾讯云计算(北京)有限责任公司 京ICP证150476号 | 京ICP备11018762号 | 京公网安备号11010802020287
Copyright © 2013 - 2025 Tencent Cloud.
All Rights Reserved. 腾讯云 版权所有