HTML 代码 CSS 省略了。
<ul>
<li data-id="a">
<span>家用电器</span>
</li>
<li data-id="b">
<span>家用电器</span>
</li>
<li data-id="c">
<span>家用电器</span>
</li>
</ul>
<div id='sub' class="none">
<div id='a' class="sub_content ">
<dl>
<dt>
<a href="">tv <i>></i></a>
</dt>
<dd>
<a href="">sdkjfhasdkjfhaskj</a>
<a href="">sdkjfhasdkjfhaskj</a>
<a href="">sdkjfhasdkjfhaskj</a>
<a href="">sdkjfhasdkjfhaskj</a>
</dd>
</dl>
</div>
<div id='b' class="sub_content ">
<dl>
<dt>
<a href="">tv <i>></i></a>
</dt>
<dd>
<a href="">sAAAAAAAAAAAAkjfhaskj</a>
<a href="">sdkjfhasdkjfhaskj</a>
<a href="">sdkjfhasdkjfhaskj</a>
<a href="">sdkjfhasdkjfhaskj</a>
</dd>
</dl>
</div>
<div id='c' class="sub_content">
<dl>
<dt>
<a href="">tv <i>></i></a>
</dt>
<dd>
<a href="">sdkBBBBBBBBBBBBBBaskj</a>
<a href="">sdkjfhasdkjfhaskj</a>
<a href="">sdkjfhasdkjfhaskj</a>
<a href="">sdkjfhasdkjfhaskj</a>
</dd>
</dl>
</div>
</div>
</div>
JS 代码 使用Jquery 利用mousetern mouseout时间 事件委托
$( () => {
$(() => {
// 子菜单
let sub = $('#sub');
// 主菜单选中
let activeRow;
// 子菜单选中
let activeMenu;
// 保存定时器
let timer;
// 预测用户是不是想移入二级菜单 li标签到菜单的上边缘 和 下边缘组成一个三角形,利用向量判断是不是绘制了一个三角形
// 追踪鼠标的 位置 只需要三个值
let mouseTrack = []
// 把鼠标绑定的事件绑定在document上
mouseHandler = (e) => {
mouseTrack.push({
x: e.pageX,
y: e.pageY
})
// 只保存三个值 大于三个值 去掉第一个值
if (mouseTrack.length > 3) {
mouseTrack.shift();
}
}
$('#test')
.on("mouseenter", () => {
sub.removeClass('none');
$(document).bind("mousemove", mouseHandler)
})
.on("mouseleave", () => {
sub.addClass('none');
// 鼠标移动事件的解绑
$(document).unbind("mousemove", mouseHandler);
})
// 事件委托
.on('mouseenter', 'li', (e) => {
// 当鼠标从一个li到另一个li menu会马上跟着变化 用户就无法点击 利用定时器
if (timer) {
clearTimeout(timer);
}
// 第一次鼠标移入
if (!activeRow) {
activeRow = $(e.target).addClass('active');
activeMenu = $("#" + activeRow.data("id"));
activeMenu.removeClass("none");
}
let currenMouse = mouseTrack[2];
// 上一次的鼠标位置
let leftCorner = mouseTrack[1];
let delay = needDealy(sub,leftCorner,currenMouse) ;
// 如果用户准备移入对应的子菜单
if(delay) {
timer = setTimeout(() => {
// 鼠标移入另外一个li
activeRow.removeClass('active');
activeMenu.addClass('none');
// 重新指向选中的li
activeRow = $(e.target);
activeMenu = $("#" + activeRow.data("id"));
activeRow.addClass("active");
activeMenu.removeClass("none");
timer = null;
}, 300);
}else{
var perActiveRow = activeRow;
var perActiveMenu = activeMenu;
activeRow = $(e.target);
activeMenu = $('#' + activeRow.data('id'));
perActiveRow.removeClass('active');
perActiveMenu.addClass('none');
activeRow.addClass('active');
activeMenu.removeClass('none')
}
});
// 判断用户鼠标是否准备移入当前选中的li对应的menu 判断当前鼠标位置是否在 与当面位置上一次的位置 与菜单的左上角和左下角组成的三角形中 利用三个向量乘积是否大于0
// 向量计算的工具函数
// 计算2个坐标点的构成向量
function vector(a, b) {
return {
x: a.x - b.x,
y: a.y - b.y
}
}
// 向量乘积
function vectorProduct(a, b) {
return a.x * b.y - a.y * b.x;
}
// 判断是否在三角形内
function isPositionInAngle(p, a, b, c) {
let pa = vector(p, a);
let pb = vector(p, b);
let pc = vector(p, c);
let t1 = vectorProduct(pa, pb);
let t2 = vectorProduct(pa, pc);
let t3 = vectorProduct(pb, pc);
return t1 * t2 * t3 > 0;
// 如果大于0 表示在三角形内
}
// 判断用户是否会移入当选选中li 对应的子菜单
// ele 菜单 leftCorner 鼠标上一次的位置 currenMouse鼠标当前位置
function needDealy(ele, leftCorner, currenMouse) {
// 菜单的位置
let offset = ele.offset();
// 菜单左上角的位置
let topLeft = {
x: offset.left,
y: offset.top
}
// 菜单左下角那个点的位置
let bottomLeft = {
x: offset.left,
y: offset.top + ele.height
}
return isPositionInAngle(currenMouse,leftCorner,topLeft,bottomLeft) ;
}
})
这样子就可以做出完美的菜单栏无延迟的切换了.
知识点 事件委托 提前判断用户行为
网友评论