在日常的開發(fā)中牢硅,可能會(huì)遇到要做“智能照片翻滾”的需求,具體效果如圖:
就是隨著鼠標(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的平移效果)上邊界距離文檔窗口上邊緣、左邊界距離文檔窗口左邊緣的距離钧椰,如圖:
兩者之差:
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)系的平移
假設(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)值。如圖:
此時(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)起來了宪巨,如圖:
在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();