昨天老大說讓我寫一個判斷鼠標從什么方向進入容器的js插件。第一反應就是算出進入容器的點的坐標,然后與四個邊線的位置比較。可是又覺得這樣的想法好老土,沒有一點技巧性。
在網上搜索發現有一個很多人都轉載的算法,如下:
1 $("#wrap").bind("mouseenter mouseleave",function(e) { 2 var w = $(this).width(); 3 var h = $(this).height(); 4 var x = (e.pageX - this.offsetLeft - (w / 2)) * (w > h ? (h / w) : 1); 5 var y = (e.pageY - this.offsetTop - (h / 2)) * (h > w ? (w / h) : 1); 6 var direction = Math.round((((Math.atan2(y, x) * (180 / Math.PI)) + 180) / 90) + 3) % 4; //direction的值為“0,1,2,3”分別對應着“上,右,下,左” 7 var eventType = e.type; 8 var dirName = new Array('上方','右側','下方','左側'); 9 if(e.type == 'mouseenter'){ 10 $("#result").html(dirName[direction]+'進入'); 11 }else{ 12 $('#result').html(dirName[direction]+'離開'); 13 } 14 });
就這么幾行代碼,將我印象里會有一長串的if else或者switch來完成的事完美的解決掉。在感到神奇之后,更多的是不解,因為看不懂。在網上搜索一番,也沒有找到有誰來分析這個算法。於是自己研究了一下,現在寫下來,作為自己的一份學習筆記,也希望能夠和大家分享。
有不對的地方,或者有更好的理解,請告訴我,謝謝!
原理:以div容器的中心點作為圓心,以高和寬的最小值作為直徑畫圓,將圓以[π/4,3π/4),[3π/4,5π/4),[5π/4,7π/4),[-π/4,π/4)划分為四個象限,鼠標進入容器時的點的atan2(y,x)值在這四個象限里分別對應容器邊框的下,右,上,左。如下圖所示

var x = (e.pageX - this.offsetLeft - (w / 2)) * (w > h ? (h / w) : 1);計算x坐標值時,如果點原來的x坐標的絕對值大於圓的半徑值,則按 h/w 這個比例進行縮小,使得到的點的位置在容器的邊界位置所對應的象限區間里。
y 坐標的計算也是一樣。
var direction = Math.round((((Math.atan2(y, x) * (180 / Math.PI)) + 180) / 90) + 3) % 4;
((Math.atan2(y, x) * (180 / Math.PI)將點的坐標對應的弧度值換算成角度度數值,這里加上180並非必要,只是為了使得到的0,1,2,3能夠與習慣性的上,右,下,左的位置對照,如果不加上180,得到的0,1,2,3就會分別對應下,右,上,左。
*修正:這里加上180不是並非必要的,而是在這個算法里必須的,因為atan2(y,x)返回值的范圍是[-π,π],而不是我們習慣的[0,2π],負值會影響結果的正確性(比如右上和右下算出來的結果會不同),而且確實也使得得到的結果0,1,2,3的順序符合了習慣(原作者可能沒想這個,只是css里總是這個順序,或許是我自己的習慣~)。
除以90,再取四舍五入值,是一個很精妙的用法,使得可以以45°為分界線。
分析完這個算法后,我內心有些激動。這里並沒有用到什么高深的知識,即使是三角函數的基礎知識都可以不知道(雖然初中就學了),但是可以用這么短短的三兩行代碼,就把我腦袋里浮現的那一長串的if else拋向雲和山的彼端去,真的讓我很難抑制內心的激動,深深的體會到算法之美,數學之美。
ps:以后一定要多多驗證了再做結論,粗心不是個好態度啊!
