判斷鼠標(biāo)在一個(gè)DOM元素上“進(jìn)入/移出”的方向

在日常的開發(fā)中牢硅,可能會(huì)遇到要做“智能照片翻滾”的需求,具體效果如圖:


鼠標(biāo)移入移出方向判斷-效果圖.jpg

就是隨著鼠標(biāo)從不同方向移動(dòng)到圖片上送挑,圖片根據(jù)移入方向進(jìn)行帶有3D效果的旋轉(zhuǎn)千元,移除時(shí)同樣根據(jù)方向轉(zhuǎn)回有圖片的那一面。
那么蔓姚,這個(gè)dom元素上移入/移出的方向是如何判斷的呢捕虽?

一、使用什么事件來監(jiān)聽鼠標(biāo)移入移出

在這里坡脐,鼠標(biāo)移入dom元素用mouseenter事件監(jiān)聽泄私,移出dom元素用mouseleave事件監(jiān)聽。

二、判斷鼠標(biāo)的位置

我們知道挖滤,上面兩個(gè)鼠標(biāo)事件中崩溪,有個(gè)屬性叫e.clientx和e.clientY,分別代表鼠標(biāo)以文檔窗口左上角為坐標(biāo)原點(diǎn)斩松,往右為x軸正方向伶唯,往下為y軸正方向的距離。
同時(shí)惧盹,dom元素有兩個(gè)屬性offsetTop和offsetLeft乳幸,分別代表元素(不包含transform的平移效果)上邊界距離文檔窗口上邊緣、左邊界距離文檔窗口左邊緣的距離钧椰,如圖:


鼠標(biāo)移入移出方向判斷-鼠標(biāo)位置.jpg

兩者之差:
dx = clientX - offsetLeft
dy = clientY - offsetTop
就代表了觸發(fā)鼠標(biāo)mouseenter事件的點(diǎn)粹断,距離dom元素左上頂點(diǎn)的距離。相當(dāng)于dom元素上嫡霞,有個(gè)坐標(biāo)系瓶埋,(dx, dy)就是在這個(gè)坐標(biāo)系下的值,如圖:


鼠標(biāo)移入移出方向判斷-坐標(biāo)系1.jpg
三诊沪、坐標(biāo)系的平移

假設(shè)养筒,我們將上述的坐標(biāo)系進(jìn)行平移,使它的坐標(biāo)原點(diǎn)處于dom元素的中心點(diǎn)端姚,那么x晕粪、y方向分別要減去寬高一半即width/2,height/2的距離渐裸。
dx = clientX - offsetLeft - width/2
dy = clientY - offfsetTop - height/2
那么(dx, dy)就相當(dāng)于鼠標(biāo)移入事件觸發(fā)的點(diǎn)巫湘,在以元素中心點(diǎn)為原點(diǎn)的新坐標(biāo)系下的坐標(biāo)值。如圖:


鼠標(biāo)移入移出方向判斷-新坐標(biāo)系.jpg

此時(shí)昏鹃,坐標(biāo)值有正有負(fù)尚氛,從元素上方移入的時(shí)候,dy的值都是負(fù)的盆顾;從元素右側(cè)移入的時(shí)候怠褐,dx都是正的有;以此類推您宪。

四奈懒、將移入方向與角度對(duì)應(yīng)起來

將dom元素的四個(gè)頂點(diǎn)與坐標(biāo)系的原點(diǎn)連線,那么可以看到移入移出元素的四個(gè)方向就分別與四個(gè)角度值對(duì)應(yīng)起來了宪巨,如圖:


鼠標(biāo)移入移出方向判斷-方向與角度值對(duì)應(yīng).jpg

在js中磷杏,Math.atan2(y, x)方法可返回從x軸到點(diǎn)(x, y)之間的角度(弧度為單位),返回值 -Π 到 Π(Math.PI)捏卓。
1极祸、θ = Math.atan2(dy, dx) * 180 / Math.PI 則返回了以角度為單位的值慈格。
此時(shí)四邊分別對(duì)應(yīng)的角度為:
上邊:θ = [-135°, -45°]
右邊:θ = [-45°, 45°]
下邊:θ = [45°, 135°]
左邊:θ = [-180°, -135°] [135°, 180°]
2、θ范圍為-180到180間遥金,將它轉(zhuǎn)化為0到360間浴捆,加上180
即θ = Math.atan2(dy, dx) * 180 / Math.PI + 180,此時(shí)四邊分別對(duì)應(yīng)角度為:
上邊:θ = [45°, 135°]
右邊:θ = [135°, 225°]
下邊:θ = [225°, 315°]
左邊:θ = [0°, 45°] [315°, 360°]
3稿械、θ的結(jié)果如果同時(shí)除以90选泻,并且四舍五入,即
θ = Math.round((Math.atan2(dy, dx) * 180 / Math.PI + 180) / 90)美莫,此時(shí)四邊分別對(duì)應(yīng)的值為:
上邊:θ = 1
右邊:θ = 2
下邊:θ = 3
左邊:θ = 0 | 4
4页眯、上面的結(jié)果再次加3,即
θ = Math.round((Math.atan2(dy, dx) * 180 / Math.PI + 180) / 90) + 3厢呵,此時(shí)四邊分別對(duì)應(yīng)的值為:
上邊:θ = 4
右邊:θ = 5
下邊:θ = 6
左邊:θ = 3 | 7
5窝撵、最后,θ結(jié)果再次對(duì)4取余襟铭,即
θ = (Math.round((Math.atan2(dy, dx) * 180 / Math.PI + 180) / 90) + 3) % 4碌奉,此時(shí)四邊分別對(duì)應(yīng)的值為:
上邊:θ = 0
右邊:θ = 1
下邊:θ = 2
左邊:θ = 3
至此,完成了鼠標(biāo)移入的坐標(biāo)點(diǎn)寒砖,通過一系列計(jì)算步驟道批,一一對(duì)應(yīng)到四個(gè)方向的過程。

五入撒、若是dom元素不是正方形

以上的判斷和計(jì)算,都是假設(shè)如果dom元素是正方形的情況下椭岩,如果dom元素不是正方形茅逮,那么dx、dy的計(jì)算應(yīng)為:
dx = (clientX - offsetLeft - width / 2) * (width > height ? (height / width ) : 1);
dy = (clientY - offfsetTop - height / 2) * (height > width ? (width /height ) : 1);

判斷方向的核心代碼
/**
* 根據(jù)鼠標(biāo)移入移出事件中鼠標(biāo)的位置判哥,來判斷它是在元素的哪個(gè)方向移入移出
* 返回值 0 代表從上方移入移出献雅,1 代表從右側(cè)移入移出,2 代表從下方移入移出塌计,3 代表從左側(cè)移入移出
*/
function getDirection(event) {
    let d;
    let w = dom.offsetWidth;
    let h = dom.offsetHeight;
    let l = dom.offsetLeft;
    let t = dom.offsetTop;
    let dx = (event.clientX - l - w / 2) * (w > h ? (h / w) : 1);
    let dy = (event.clientY - t - h / 2) * (h > w ? (w / h) : 1);
    d = (Math.round((Math.atan2(dy, dx) * 180 / Math.PI + 180) / 90) + 3) % 4;
    return d;
}

以下是測(cè)試代碼:

let dom = document.getElementById('test');
function bindEvent() {
    dom.onmouseenter = function(e) {
        get(e, 'in');
    }
    
    dom.onmouseleave = function(e) {
        get(e, 'out');
    }
}

function get(e, state) {
    let d = getDirection(e);
    let dir = '';
    switch (d) {
        case 0: {
            dir = '-top';
            break;
        }
        case 1: {
            dir = '-right';
            break;
        }
        case 2: {
            dir = '-bottom';
            break;
        }
        case 3: {
            dir = '-left';
            break;
        }
    }
    console.log(state + dir);
}


/**
* 根據(jù)鼠標(biāo)移入移出事件中鼠標(biāo)的位置挺身,來判斷它是在元素的哪個(gè)方向移入移出
* 返回值 0 代表從上方移入移出,1 代表從右側(cè)移入移出锌仅,2 代表從下方移入移出章钾,3 代表從左側(cè)移入移出
*/
function getDirection(event) {
    let d;
    let w = dom.offsetWidth;
    let h = dom.offsetHeight;
    let l = dom.offsetLeft;
    let t = dom.offsetTop;
    let dx = (event.clientX - l - w / 2) * (w > h ? (h / w) : 1);
    let dy = (event.clientY - t - h / 2) * (h > w ? (w / h) : 1);
    d = (Math.round((Math.atan2(dy, dx) * 180 / Math.PI + 180) / 90) + 3) % 4;
    return d;
}

bindEvent();
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個(gè)濱河市热芹,隨后出現(xiàn)的幾起案子贱傀,更是在濱河造成了極大的恐慌,老刑警劉巖伊脓,帶你破解...
    沈念sama閱讀 217,406評(píng)論 6 503
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件府寒,死亡現(xiàn)場(chǎng)離奇詭異,居然都是意外死亡,警方通過查閱死者的電腦和手機(jī)株搔,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,732評(píng)論 3 393
  • 文/潘曉璐 我一進(jìn)店門剖淀,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人纤房,你說我怎么就攤上這事纵隔。” “怎么了帆卓?”我有些...
    開封第一講書人閱讀 163,711評(píng)論 0 353
  • 文/不壞的土叔 我叫張陵巨朦,是天一觀的道長(zhǎng)。 經(jīng)常有香客問我剑令,道長(zhǎng)糊啡,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 58,380評(píng)論 1 293
  • 正文 為了忘掉前任吁津,我火速辦了婚禮棚蓄,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘碍脏。我一直安慰自己梭依,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,432評(píng)論 6 392
  • 文/花漫 我一把揭開白布典尾。 她就那樣靜靜地躺著役拴,像睡著了一般。 火紅的嫁衣襯著肌膚如雪钾埂。 梳的紋絲不亂的頭發(fā)上河闰,一...
    開封第一講書人閱讀 51,301評(píng)論 1 301
  • 那天,我揣著相機(jī)與錄音褥紫,去河邊找鬼姜性。 笑死,一個(gè)胖子當(dāng)著我的面吹牛髓考,可吹牛的內(nèi)容都是我干的部念。 我是一名探鬼主播,決...
    沈念sama閱讀 40,145評(píng)論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼氨菇,長(zhǎng)吁一口氣:“原來是場(chǎng)噩夢(mèng)啊……” “哼儡炼!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起门驾,我...
    開封第一講書人閱讀 39,008評(píng)論 0 276
  • 序言:老撾萬榮一對(duì)情侶失蹤射赛,失蹤者是張志新(化名)和其女友劉穎,沒想到半個(gè)月后奶是,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體楣责,經(jīng)...
    沈念sama閱讀 45,443評(píng)論 1 314
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡竣灌,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,649評(píng)論 3 334
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了秆麸。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片初嘹。...
    茶點(diǎn)故事閱讀 39,795評(píng)論 1 347
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖沮趣,靈堂內(nèi)的尸體忽然破棺而出屯烦,到底是詐尸還是另有隱情,我是刑警寧澤房铭,帶...
    沈念sama閱讀 35,501評(píng)論 5 345
  • 正文 年R本政府宣布驻龟,位于F島的核電站,受9級(jí)特大地震影響缸匪,放射性物質(zhì)發(fā)生泄漏翁狐。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,119評(píng)論 3 328
  • 文/蒙蒙 一凌蔬、第九天 我趴在偏房一處隱蔽的房頂上張望露懒。 院中可真熱鬧,春花似錦砂心、人聲如沸懈词。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,731評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽坎弯。三九已至,卻和暖如春译暂,著一層夾襖步出監(jiān)牢的瞬間荞怒,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 32,865評(píng)論 1 269
  • 我被黑心中介騙來泰國打工秧秉, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人衰抑。 一個(gè)月前我還...
    沈念sama閱讀 47,899評(píng)論 2 370
  • 正文 我出身青樓象迎,卻偏偏與公主長(zhǎng)得像,于是被迫代替她去往敵國和親呛踊。 傳聞我的和親對(duì)象是個(gè)殘疾皇子砾淌,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,724評(píng)論 2 354

推薦閱讀更多精彩內(nèi)容