一、鼠标进入容器方向的判定
判断鼠标从哪个方向进入元素容器是一个经常碰到的问题,如何来判断呢?
首先想到的是:获取鼠标的位置,然后经过一大堆的if..else
逻辑来确定。这样的做法比较繁琐,下面介绍两种比较方便的方法:
第一种方法,利用圆和反正切三角函数
如下图所示:
以div容器的中心点作为圆心,以高和宽的最小值作为直径画圆,将圆以[π/4,3π/4),[3π/4,5π/4),[5π/4,7π/4),[-π/4,π/4)划分为四个象限。
代码如下:
var w = $(this).width();
var h = $(this).height();
/ 计算X和Y相对于圆心点的距离,如果不是正方形,按照X,Y谁小按谁进行比例缩放/
var x = (e.pageX - $(this).offset().left - (w/2)) ( w > h ? (h/w) : 1 );
var y = (e.pageY - $(this).offset().top - (h/2)) ( h > w ? (w/h) : 1 );
/ 根据X,Y的值,做反正切atan2计算,返回值在[-π,π]之间,这里加上180,剔除负值/
/ 如果不加180,则0,1,2,3对应下左上右/
/ 除以90并四舍五入,使得可以以45度为分割线,获取象限/
/ 加3与4取模,将0,1,2,3对应t,r,b,l既上右下左/
var direction = Math.round((((Math.atan2(y,x) * (180 / Math.PI)) + 180 ) / 90 )+3)%4;
switch(direction) {
case 0:
/ 上 /
break;
case 1:
/ 右 /
break;
case 2:
/ 下 /
break;
case 3:
/ 左 /
break;
}});
这个方法中的Math.round((((Math.atan2(y,x) * (180 / Math.PI)) + 180 )/90)+3)% 4
公式比较难理解,首先得到鼠标坐标经过换算后的值,然后算出该坐标的弧度,接着换算成度数,加180去掉负数,随后转移象限将0123对应TRBL,如果不加180去掉负数,0123对应BLTR,有点不合CSS的习惯。
第二种方法,利用斜率
如下图所示:
以浏览器左上角做原点,画坐标轴,向下为负,向右为正,和数学坐标系一致。中间的div的左上角坐标(x1,y1),右下角坐标(x2,y2),中心点的坐标(cx,cy)。如图两点的斜率为k(k<0),关于x轴对称的斜率为-k。
需要注意一点的是所有的Y轴坐标都是负数,因为就是将容器置于坐标系的第四象限。
如上代码所示:当鼠标的位置与容器中心点所形成的斜率在(k,-K)之间,必然是左右移入或移出,如果鼠标的X坐标大于中心点CX,则是右边进入,否则为左边进入;若斜率不在(k,-k)之间,则是上下进入或出去,只要判断鼠标的Y坐标与中心点CY的大小关系即可,大于则是下边,相反就是上边。
二、window.open新窗口被拦截的问题
当我们使用window.open()
方法打开一个窗口时,部分浏览器会检测是否是用户主动行为,若不是,则会阻止窗口的打开,例如在异步Ajax的回调函数中调用。
新窗口被拦截检测
窗口被阻止打开,如不给出提示,用户体验将会很不好,那如何检测窗口被阻止?
如下代码所示:
为何新窗口被拦截
浏览器设计者出于安全的考虑,window.open
命令在用户操作(trusted events)时,才会正常的打开应该页面而不会被浏览器拦截。什么是trusted events?
The isTrusted read-only property of the Event interface is a boolean that is true when the event was generated by a user action,and false when the event was created or modified by a script or dispatched via dispatchEvent.
当前事件是由用户行为触发(例如鼠标点击按钮触发操作),便是trusted events,而用自定义事件dispatchEvent触发的事件则不是trusted events。
因此使用JS代码自动触发window.open()
,第二个参数不为_self,打开新窗口在大部分浏览器中会被拦截。如果第二个参数为_self,则不会被拦截,如window.open("")
。
如何Ajax回调中避免被拦截
很多人的需求是点击按钮发送Ajax请求,请求数据回来后,再使用window.open来打开新的窗口,由于是异步操作,直接window.open
,肯定会被拦截。这时我们可以变通以下,先打开一个空窗口,然后等数据回来后替换为需要的地址
如下所示: