UDN-企业互联网技术人气社区

板块导航

浏览  : 359
回复  : 2

[原生js] JS判断鼠标进入容器方向的方法等

[复制链接]
葡萄柚的头像 楼主
发表于 2016-12-30 15:02:52 | 显示全部楼层 |阅读模式
  JS判断鼠标进入容器方向的方法和分析window.open新窗口被拦截的问题

  1、鼠标进入容器方向的判定

  判断鼠标从哪个方向进入元素容器是一个经常碰到的问题,如何来判断呢?首先想到的是:获取鼠标的位置,然后经过一大堆的 if..else 逻辑来确定。这样的做法比较繁琐,下面介绍两种比较方便的方法:

  第一种方法,利用圆和反正切三角函数

  如下图所示:
2.png

  以div容器的中心点作为圆心,以高和宽的最小值作为直径画圆,将圆以[π/4,3π/4),[3π/4,5π/4),[5π/4,7π/4),[-π/4,π/4)划分为四个象限。

  代码如下:
  1. $(".box").on("mouseenter mouseleave",function(e){

  2. /** 获取容器宽高 **/
  3. var w = $(this).width();
  4. var h = $(this).height();

  5. /** 计算X和Y相对于圆心点的距离,如果不是正方形,按照X,Y谁小按谁进行比例缩放**/
  6. var x = (e.pageX - $(this).offset().left - (w/2)) * ( w > h ? (h/w) : 1 );
  7. var y = (e.pageY - $(this).offset().top  - (h/2)) * ( h > w ? (w/h) : 1 );

  8. /** 根据X,Y的值,做反正切atan2计算,返回值在[-π,π]之间 ,这里加上180,剔除负值**/
  9. /** 如果不加180,则0,1,2,3对应下左上右**/
  10. /** 除以90并四舍五入,使得可以以45度为分割线,获取象限**/
  11. /** 加3与4取模,将0,1,2,3对应t,r,b,l既上右下左**/
  12. var direction = Math.round((((Math.atan2(y, x) * (180 / Math.PI)) + 180 ) / 90 )+3)%4;
  13. switch(direction) {
  14. case 0:
  15.   /** 上 **/
  16. break;
  17. case 1:
  18.   /** 右 **/
  19. break;
  20. case 2:
  21.   /** 下 **/
  22. break;
  23. case 3:
  24.   /** 左 **/
  25. break;
  26. }});
复制代码

  这个方法中的 Math.round((((Math.atan2(y, x) * (180 / Math.PI)) + 180 )/90)+3)% 4 公式比较难理解,首先得到鼠标坐标经过换算后的值,然后算出该坐标的弧度,接着换算成度数,加180去掉负数,随后转移象限将0123对应TRBL,如果不加180去掉负数,0123对应BLTR,有点不合CSS的习惯。

  第二种方法,利用斜率

  如下图所示:
1.png

  以浏览器左上角做原点,画坐标轴,向下为负,向右为正,和数学坐标系一致。中间的div的左上角坐标(x1,y1),右下角坐标(x2,y2),中心点的坐标(cx,cy)。如图两点的斜率为k(k<0),关于x轴对称的斜率为-k。

  需要注意一点的是所有的Y轴坐标都是负数,因为就是将容器置于坐标系的第四象限。
  1. $(".box").on("mouseenter mouseleave", function(e) {
  2.   var w = $(this).width();
  3.       h = $(this).height(),
  4.       x1 = $(this).offset().left,
  5.       y1 = -$(this).offset().top,
  6.       x2 = x1 + w,
  7.       y2 = y1 - h,
  8.       cx = (x1 + x2) / 2,
  9.       cy = (y1 + y2) / 2,
  10.       k = (y2 - y1) / (x2 - x1),
  11.       k1 = (-e.pageY - cy) / (e.pageX - cx),
  12.       direction = -1;
  13.   if ((k1 < -k) && (k < k1)) {
  14.     direction = e.pageX > cx?1:3;
  15.   } else {
  16.     direction = -e.pageY > cy?0:2;//大家理解代码的时候一定记住,Y坐标都是负的
  17.   }
  18.   //0123对应TRBL
  19. });
复制代码

  如上代码所示:当鼠标的位置与容器中心点所形成的斜率在(k,-K)之间,必然是左右移入或移出,如果鼠标的X坐标大于中心点CX,则是右边进入,否则为左边进入;若斜率不在(k,-k)之间,则是上下进入或出去,只要判断鼠标的Y坐标与中心点CY的大小关系即可,大于则是下边,相反就是上边。

  2、window.open新窗口被拦截的问题

  当我们使用 window.open() 方法打开一个窗口时,部分浏览器会检测是否是用户主动行为,若不是,则会阻止窗口的打开,例如在异步Ajax的回调函数中调用。

  新窗口被拦截检测

  窗口被阻止打开,如不给出提示,用户体验将会很不好,那如何检测窗口被阻止?如下代码所示:
  1. var newWin = null,
  2.     isBlock = !1;
  3. /** 新窗口被某些扩展阻止打开,会抛出错误,因此使用try..catch **/
  4. try {
  5.     newWin = window.open('http://www.baidu.com', '_blank');
  6.     /** 新窗口被阻止时,返回值是undefined或null**/
  7.     (!newWin) && (isBlock = !0);
  8. } catch (ex) {
  9.     isBlock = !0;
  10. }
  11. if (isBlock) alert('您阻止了窗口的打开。');
复制代码

  为何新窗口被拦截

  浏览器设计者出于安全的考虑,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("http://www.baidu.com","_self") 。

  如何Ajax回调中避免被拦截

  很多人的需求是点击按钮发送Ajax请求,请求数据回来后,再使用window.open来打开新的窗口,由于是异步操作,直接window.open,肯定会被拦截。这时我们可以变通以下,先打开一个空窗口,然后等数据回来后替换为需要的地址,如下所示:
  1. <!DOCTYPE html>
  2. <html lang="en">
  3. <head>
  4.     <meta charset="UTF-8">
  5.     <title>弹窗拦截测试</title>
  6.     <style type="text/css">
  7.         #btn{ width:100px; height: 30px; line-height: 30px; text-align:center; background-color:#0087dc; transition:all .2s; color:#fff; border-radius:3px;cursor:pointer; }
  8.         #btn:hover{ background-color:#0060b2; }
  9.     </style>
  10. </head>
  11. <body>
  12.     <div id="btn">打开新窗口</div>
  13.     <script type="text/JavaScript">
  14.         btn.addEventListener('click',(e)=>{
  15.             var xhr = new XMLHttpRequest();
  16.             var newWin = window.open('about:blank');
  17.             xhr.onreadystatechange = ()=>{
  18.                 if(xhr.readyState == 4){
  19.                     if(xhr.status == 200){
  20.                         newWin.location.href="http://www.baidu.com";
  21.                     }
  22.                 }
  23.             };
  24.             xhr.open('post','/dnslookup',!1);//异步方式
  25.             xhr.setRequestHeader('content-type','application/x-www-form-urlencoded');
  26.             xhr.send('host=www.baidu.com&rrtype=A');
  27.         },!0);
  28.     </script>
  29. </body>
  30. </html>
复制代码

  服务端代码如下:
  1. var http = require('http'),
  2.     url  = require('url'),
  3.     dns  = require('dns'),
  4.     qs   = require('querystring'),
  5.     fs   = require('fs');

  6. function router(req,res,pathname){
  7.     switch(pathname){
  8.         case '/dnslookup':
  9.             lookup(req,res);
  10.             break;
  11.         default:
  12.             showIndex(req,res);
  13.     }
  14. }
  15. function showIndex(req,res){
  16.     var pagePath = __dirname+'/'+'block.html';
  17.     var html = fs.readFileSync(pagePath);
  18.     res.end(html);
  19. }
  20. function lookup(req,res){
  21.     var postData = '';
  22.     req.on('data',function(data){
  23.         postData+=data;
  24.     });
  25.     req.on('end',function(data){
  26.         var json = qs.parse(postData);
  27.         var hostname = json.host;
  28.         var rrtype = json.rrtype;
  29.         dns.resolve(hostname,rrtype,function(err,adresses){
  30.             if(err){
  31.                 res.end(JSON.stringify({errcode:1,ips:[]}));
  32.             }
  33.             res.end(JSON.stringify({errcode:0,ips:adresses}));
  34.         });
  35.         
  36.     });
  37. }
  38. http.createServer(function(req,res){
  39.     var pathname = url.parse(req.url).pathname;
  40.     req.setEncoding("utf8");
  41.     res.writeHead(200,{'Content-Type':'text/html'});
  42.     router(req,res,pathname);
  43. }).listen(3000);
复制代码

  如上所示便可解决在Ajax回调中新窗口被拦截的问题。

相关帖子

发表于 2016-12-30 15:03:22 | 显示全部楼层
路过 帮顶 嘿嘿
使用道具 举报

回复

发表于 2016-12-31 19:18:19 | 显示全部楼层
鄙视楼下的顶帖没我快,哈哈
使用道具 举报

回复

您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

关于我们
联系我们
  • 电话:010-86393388
  • 邮件:udn@yonyou.com
  • 地址:北京市海淀区北清路68号
移动客户端下载
关注我们
  • 微信公众号:yonyouudn
  • 扫描右侧二维码关注我们
  • 专注企业互联网的技术社区
版权所有:用友网络科技股份有限公司82041 京ICP备05007539号-11 京公网网备安1101080209224 Powered by Discuz!
快速回复 返回列表 返回顶部