先來波廣告,DataWorks全新起航,提供給大家更優的資料開發體驗,有任何建議歡迎回報。
問題:
廢話不多說,看一下優化之前的菜單欄顯示的互動。

是的,我就是想直接選中資料開發,怎麼就這麼難。
這一塊整體的互動就是hover到上面的圖示,然後出現産品菜單,第一版在實作的時候,用css去控制菜單的display屬性,是以就會導緻當圖示失去焦點的時候,菜單就立馬消失了。
.logo:hover .list {
display: none;
}
解決思路:
一種常見的解決方式就是設定延時,失去焦點後不會立馬消失。這種做法有一個缺點就是當使用者真的是想收起菜單時,還是要經過一段延時。
經過主管和師兄的指導,去看了下亞馬遜的菜單實作,他們的問題和這個相似,就是如何判斷使用者是要切換子菜單還是想移動到子菜單中,具體的互動如下:
可以看到在移動到子菜單的過程中,會經過下面的一級菜單,但是子菜單的内容并沒有發生變化。具體的實作過程,已經單獨抽成了一個jquery的元件,
https://github.com/kamens/jQuery-menu-aim原了解析:
學習了下實作的代碼,其核心就是認為當下一刻的滑鼠軌迹在這個藍色三角區域的時候,使用者是想移動到子菜單的。
仔細想了下,确實如果使用者是想切換一級菜單的時候,滑鼠的軌迹一般是會往正下或者正上方向去滑動。
将這個原理應用到現在的場景中,假設目前的滑鼠位置為A,那麼如果下一刻滑鼠的位置在這個紅色區域内,就認為使用者是想移動到菜單中。
那麼怎麼判斷下一刻滑鼠的位置是在三角形内,方法有很多,最簡單的判定方法就是,AB與BC的夾角e'1<e1,AC與BC的夾角 e'2<e2。
代碼實作:
根據上面分析的原理,進行代碼實作。
export default class MenuAim {
constructor(hoverDom, menuDom) {
this.hoverDom = hoverDom; // icon
this.menuDom = menuDom; // product-list
this.mouseTrack = []; // 記錄滑鼠的移動軌迹,最多隻記錄三組資料
this.onMouseOver = this.onMouseOver.bind(this);
this.onMouseLeave = this.onMouseLeave.bind(this);
this.onMouseMove = this.onMouseMove.bind(this);
this.hoverDom.addEventListener('mouseover', this.onMouseOver);
this.hoverDom.addEventListener('mouseleave', this.onMouseLeave);
this.hoverDom.addEventListener('mousemove', this.onMouseMove);
this.menuDom.addEventListener('mouseleave', () => {
this.menuDom.style.display = 'none';
});
}
onMouseOver() {
this.menuDom.style.display = 'block';
}
onMouseMove(e) {
this.mouseTrack.push({ x: e.pageX, y: e.pageY });
if (this.mouseTrack.length > 3) {
this.mouseTrack.shift();
}
}
onMouseLeave(e) {
// 滑鼠的目前位置
const currentPosition = {
x: e.pageX,
y: e.pageY,
};
// 滑鼠的上一位置
const prevPosition = this.mouseTrack[0];
// 下拉菜單的左上角
const menuLeftTop = {
x: this.menuDom.offsetLeft,
y: this.menuDom.offsetTop,
};
// 下拉菜單的右上角
const menuRightTop = {
x: this.menuDom.offsetLeft + this.menuDom.offsetWidth,
y: this.menuDom.offsetTop,
};
// 現在的位置與左上角的角度 負值
const currentLeftScale = (menuLeftTop.y - currentPosition.y) / (menuLeftTop.x - currentPosition.x);
// 現在的位置與右上角的角度
const currentRightScale = (menuRightTop.y - currentPosition.y) / (menuRightTop.x - currentPosition.x);
// 上一位置與左上角的角度 負值
const prevLeftScale = (menuLeftTop.y - prevPosition.y) / (menuLeftTop.x - prevPosition.x);
// 上一位置與右上角的角度
const prevRightScale = (menuRightTop.y - prevPosition.y) / (menuRightTop.x - prevPosition.x);
if (currentLeftScale > prevLeftScale && currentRightScale < prevRightScale) {
// 認為使用者是要移到下拉菜單
this.menuDom.style.display = 'block';
} else {
this.menuDom.style.display = 'none';
}
}
}
優化結果:
最後看一下,優化後的效果。