目的: 实现下图的效果.
效果展示
- 基本思路
左边创建一个列表
右边创建响应的弹出框
鼠标给到mouseenter事件对应的展示右边的弹出框
代码如下:
HTML代码:
<div class="section" id="news">
<!--菜单-->
<div class="menu">
<ul>
<li class="item_top" data-id="item"><a href="javascript:void(0);"><i class="icon"></i><span>行业分类</span></a></li>
<li data-id="item1"><a href="javascript:void(0);"><i class="icon"></i><span>工业机械</span></a></li>
<li data-id="item2"><a href="javascript:void(0);"><i class="icon"></i><span>洗衣机</span></a></li>
<li data-id="item3"><a href="javascript:void(0);"><i class="icon"></i><span>家用电器</span></a></li>
<li data-id="item4"><a href="javascript:void(0);"><i class="icon"></i><span>工业</span></a></li>
<li data-id="item5"><a href="javascript:void(0);"><i class="icon"></i><span>化工</span></a></li>
<li data-id="item6"><a href="javascript:void(0);"><i class="icon"></i><span>电动车</span></a></li>
<li data-id="item7"><a href="javascript:void(0);"><i class="icon"></i><span>洗衣机</span></a></li>
<li data-id="item8"><a href="javascript:void(0);"><i class="icon"></i><span>工程</span></a></li>
<li class="item_bottom" data-id="item"><a href="javascript:void(0);"><i class="icon"></i><span>定制</span></a></li>
</ul>
<!--弹出框-->
<div class="menu_pop">
<!--排序a-z-->
<div class="industy_sort"></div>
<a href=""><i class="icon multiple"></i></a>
<!--模块切换-->
<div class="industy_part" id="part_item1">0</div>
<div class="industy_part" id="part_item2">1</div>
<div class="industy_part" id="part_item3">2</div>
<div class="industy_part" id="part_item4">3</div>
<div class="industy_part" id="part_item5">4</div>
<div class="industy_part" id="part_item6">5</div>
<div class="industy_part" id="part_item7">6</div>
<div class="industy_part" id="part_item8">7</div>
<!--多选的时候下面的确定按钮-->
<div class="confirm"> </div>
<!--行业专家-->
<div class="industy_people" id="people_item1">0</div>
<div class="industy_people" id="people_item2">1</div>
<div class="industy_people" id="people_item3">2</div>
<div class="industy_people" id="people_item4">3</div>
<div class="industy_people" id="people_item5">4</div>
<div class="industy_people" id="people_item6">5</div>
<div class="industy_people" id="people_item7">6</div>
<div class="industy_people" id="people_item8">7</div>
</div>
</div>
<div class="right_wrap">
</div>
</div>
CSS代码:
#news{
background: #ffffff;
width: 100%;
max-width: 1024px;
margin: auto;
position: relative;
z-index: 2;
margin-top: -5px;
}
/*左侧菜单模块*/
#news>.menu{
width: 180px;
display: inline-block;
box-sizing: border-box;
position: relative;
box-shadow: 2px 2px 6px 2px rgb(0,0,0);
box-shadow: 2px 2px 6px 2px rgba(0,0,0,0.08);
-webkit-box-shadow: 2px 2px 6px 2px rgba(0,0,0,0.08);
}
#news>.menu>ul{
width: 100%;
}
#news>.menu>ul li{
padding-left: 14px;
line-height: 56px;
height: 56px;
}
#news>.menu>ul li.active{
background: #F03800;
}
#news>.menu>ul li.active a{
text-decoration: none;
}
#news>.menu>ul li.active a span{
color: #FFFFFF;
}
#news>.menu>ul li a{
display: block;
padding-left: 3px;
border-bottom: 1px solid #F0F0F0;
width: 100%;
}
#news>.menu>ul li a i {
display: inline-block;
width: 15px;
height: 18px;
vertical-align: middle;
margin-right: 15px;
background: red;
}
#news>.menu li.item_top{
line-height: 50px;
height: 50px;
}
#news>.msg_menu>ul li a span{
color: #666666;
font-size: 18px;
max-width: 130px;
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
display: inline-block;
vertical-align: middle;
}
#news>.menu>.menu_pop{
width: 850px;
height: 555px;
background: #FFFFFF;
position: absolute;
left: 181px;
top: 0px;
z-index: 5;
box-shadow: 0px 0px 12px 6px rgba(0,0,0,0.1);
overflow: hidden;
border-bottom-left-radius: 10px;
border-bottom-right-radius: 10px;
display: none;
}
/*#news>.menu>.menu_pop.active{*/
/*display: block;*/
/*visibility: visible;*/
/*}*/
#news>.menu>.menu_pop>.industy_part,
#news>.menu>.menu_pop>.industy_people{
display: none;
}
#news>.menu>.menu_pop>.industy_part.active {
display: block;
}
#news>.menu>.menu_pop>.industy_people.active {
display: block;
}
问题1
当我们想将鼠标移入到右侧菜单中,鼠标必须保持在当前的小块中,否则就会造成右侧的弹出框改变。用户在使用的时候经常会出现鼠标不小心移入了其他的模块,导致右边展示内容变化,得不到用户希望的结果。
- 解决方法
我们利用setTimeout这个函数来给一个延迟,这里我给的是300毫秒。延迟判断鼠标的位置,这样即便用户移入到左侧菜单的其他模块中短时间内也不会影响右侧菜单的展示。
问题2
如果我们给到了延迟,延迟也会给我们造成一些问题,如果用户并不打算往右侧的菜单中移入,只是想简单的切换左侧菜单的选项,我们给出延迟函数就会造成右侧弹出框的延迟展示,严重影响用户的体验。
- 解决办法
这里我们引出了去抖技术,这个也是目前电商网站普遍运用的技术之一。
去抖技术
什么是去抖技术?
我个人的理解就是通过对用户可能做的行为进行预测,合理运用setTimeout这个函数,进而做出不同的处理,最大程度的提高用户体验度。
下面我就拿我做的项目举例:
下面是我们需要展示给用户的菜单。
用户行为判断:
- 用户可能要从当前菜单移入到具体的弹出菜单。
- 用户打算切换当前菜单。
- 情况1分析
用户当前的鼠标是A点,如果用户希望切换到右侧菜单那么用户鼠标的移动轨迹必定会经过三角形ABC,也就是说鼠标的下一个点一定会在三角形内部。就如同上图,我们假设下一个点是P点。那么我们就可以说只要P在三角形ABC内部那么用户目的就是为了切换到右侧菜单。此时我们给用户延迟,避免切换菜单时候造成问题。
- 情况2分析
如果用户想切换左边的菜单选项,那么用户鼠标移入的方向主要是上下移动,绝对不会朝着右侧移动,所以我们可以判断如果P不在三角形ABC内部时候,那么用户目的是切换左侧选项。
所以说判断P点在不在三角形ABC内部就显得很关键。
我们这里用到了大学的判断方法,即通过向量判断,如果向量PA、向量PB的叉乘和向量PB、向量PC的叉乘以及向量PC、向量PA的叉乘最后的符号是否一致,如果符号一致那么说明P点在三角形内部。
下面是具体代码:
//向量
function vector(a,b) {
return {
x: b.x - a.x,
y: b.y - a.y
}
}
//向量的叉乘
function vectorProductor(v1,v2) {
var res = v1.x*v2.y-v2.x*v1.y;
return res;
}
//判断P是否在三角形ABC内部
function isPointInTrangle(p,a,b,c) {
var pa = vector(p, a);
var pb = vector(p, b);
var pc = vector(p, c);
var t1 = vectorProductor(pa, pb);
var t2 = vectorProductor(pb, pc);
var t3 = vectorProductor(pc, pa);
return sameSign(t1, t2) && sameSign(t2, t3);
}
//判断符号是否相同
function sameSign(a,b) {
return (a ^ b) >= 0
}
最后把代码整合一下就是最终的JS代码
//这里运用了debounce技术来处理菜单的移入移出问题
$(document).ready(function() {
var activeRow,
activeMenu,
activeIndusty,
timer,
mouseInSub = false,
mouseTrack = [],
moveHandler;
$(".menu_pop").mouseenter(function () {
mouseInSub = true;
}).mouseleave(function () {
mouseInSub = false;
});
moveHandler = function(e) {
mouseTrack.push({
x: e.pageX,
y: e.pageY
})
if (mouseTrack.length > 3) {
mouseTrack.shift();
}
}
$(".menu").mouseenter(function () {
$(".menu_pop").show();
$(document).bind('mousemove',moveHandler);
}).mouseleave(function () {
$(".menu_pop").hide();
if (activeRow) {
activeRow.removeClass('active');
activeRow = null;
}
if (activeMenu) {
activeMenu.hide();
activeMenu = null;
}
if (activeIndusty) {
activeIndusty.hide();
activeIndusty = null;
}
$(document).unbind('mousemove',moveHandler);//解绑
})
$(".menu ul li").mouseenter(function () {
if (!activeRow) {
activeRow = $(this).addClass("active");
activeMenu = $('#part_' + $(this).attr("data-id"));
activeIndusty = $("#people_" + $(this).attr("data-id"));
activeMenu.show();
activeIndusty.show();
return;
}
var that = $(this);
if (timer) {
clearTimeout(timer);
}
var currMousePos = mouseTrack[mouseTrack.length - 1];
var leftCorner = mouseTrack[mouseTrack.length - 2];
var delay = needDelay($(".menu_pop"),leftCorner,currMousePos);
if (delay) {
timer = setTimeout(function () {
if (mouseInSub) {
return;//如果在子菜单中立即返回
}
activeRow.removeClass('active');
activeMenu.hide();
activeIndusty.hide();
activeRow = that;
activeRow.addClass('active');
activeMenu = $('#part_' + that.attr("data-id"));
activeMenu.show();
activeIndusty = $("#people_" + that.attr("data-id"));
activeIndusty.show();
},300);
} else {
var preActiveRow = activeRow;
var preActiveMenu = activeMenu;
var preActiveIndusty = activeIndusty;
activeRow = that;
activeMenu = $('#part_' + that.attr("data-id"));
activeIndusty = $("#people_" + that.attr("data-id"));
preActiveRow.removeClass('active');
preActiveMenu.hide();
preActiveIndusty.hide();
activeRow.addClass('active');
activeMenu.show();
activeIndusty.show();
}
});
});
//向量
function vector(a,b) {
return {
x: b.x - a.x,
y: b.y - a.y
}
}
//向量的叉乘
function vectorProductor(v1,v2) {
var res = v1.x*v2.y-v2.x*v1.y;
return res;
}
function isPointInTrangle(p,a,b,c) {
var pa = vector(p, a);
var pb = vector(p, b);
var pc = vector(p, c);
var t1 = vectorProductor(pa, pb);
var t2 = vectorProductor(pb, pc);
var t3 = vectorProductor(pc, pa);
return sameSign(t1, t2) && sameSign(t2, t3);
}
//判断符号是否相同
function sameSign(a,b) {
return (a ^ b) >= 0
}
//是否需要延迟
function needDelay(elem,leftCorner,currMousePos) {
var offset = elem.offset();
var leftCorner = leftCorner;
var currMousePos = currMousePos;
var topLeft = {
x: offset.left,
y: offset.top
}
var bottomLeft = {
x: offset.left,
y: offset.top + elem.height()
}
return isPointInTrangle(currMousePos,leftCorner,topLeft,bottomLeft);
}