🔥码云GVP开源项目 12k star Uniapp+ElementUI 功能强大 支持多语言、二开方便! 广告
# 京东无延时菜单效果 主要知识点: * 使用向量来判断点是否在三角形内 * debounce(去抖技术)和 throttle (节流)优化事件,这里主要是debounce 代码如下: index.html ~~~ <!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>Document</title> <style> .warp { position: relative; width: 200px; left: 50px; top: 50px; } ul { padding: 15px 0px; list-style: none; background: #6c6669; color: white; border-right-width: 0; } li { display: block; height: 30px; line-height: 30px; padding-left: 12px; cursor: pointer; font-size: 14px; position: relative; } li.active { background: #999395; } li span:hover { color: #c81623; } .none { display: none; } /*子菜单的样式*/ #sub { width: 600px; position: absolute; border: 1px solid #f7f7f7; background: #f7f7f7; box-shadow: 2px 0 5px rgba(0,0,0,0.3); left: 200px; top: 0px; box-sizing: border-box; margin: 0; padding-left: 10px; } .sub_content a { font-size: 12px; color: #666; text-decoration: none; } .sub_content dd a { border-left: 1px solid #e0e0e0; padding: 0px 10; margin: 4px 0px; } .sub_content dl { overflow: hidden; } .sub_content dt { float: left; width: 70px; font-weight: bold; clear: left; position: relative; } .sub_content dd { float: left; border-top: 1px solid #eee; margin-left: 5px; margin-bottom: 5px; } .sub_content dt i { width: 4px; height: 14px; font: 400 9px/14px consolas; position: absolute; top: 5px; right: 5px; } </style> </head> <body> <!-- 定义外层容器 --> <div class="warp" id="test"> <!-- 第一菜单 --> <ul> <li data-id="a"> <span>手机 / 电话卡</span> </li> <li data-id="b"> <span>笔记本 / 平板</span> </li> <li data-id="c"> <span>电视 / 盒子</span> </li> <li data-id="d"> <span>路由器 / 智能硬件</span> </li> <li data-id="f"> <span>移动电源 / 电池 / 插电板</span> </li> <li data-id="g"> <span>耳机 / 音响</span> </li> </ul> <!-- 二级菜单 --> <div id="sub" class="none"> <div id="a" class="sub_content none"> <dl> <dt><a href="#">电视<i>&gt;</i></a></dt> <dd> <a href="#">国企合作</a> <a href="#">国企合作</a> <a href="#">国企合作</a> </dd> </dl> </div> <div id="b" class="sub_content none"> <dl> <dt><a href="#">笔记本<i>&gt;</i></a></dt> <dd> <a href="#">国企合作</a> <a href="#">国企合作</a> <a href="#">国企合作</a> </dd> </dl> </div> <div id="c" class="sub_content none"> <dl> <dt><a href="#">盒子<i>&gt;</i></a></dt> <dd> <a href="#">国企合作</a> <a href="#">国企合作</a> <a href="#">国企合作</a> </dd> </dl> </div> <div id="d" class="sub_content none"> <dl> <dt><a href="#">路由器<i>&gt;</i></a></dt> <dd> <a href="#">国企合作</a> <a href="#">国企合作</a> <a href="#">国企合作</a> </dd> </dl> </div> <div id="f" class="sub_content none"> <dl> <dt><a href="#">移动电源<i>&gt;</i></a></dt> <dd> <a href="#">国企合作</a> <a href="#">国企合作</a> <a href="#">国企合作</a> </dd> </dl> </div> <div id="g" class="sub_content none"> <dl> <dt><a href="#">耳机<i>&gt;</i></a></dt> <dd> <a href="#">国企合作</a> <a href="#">国企合作</a> <a href="#">国企合作</a> </dd> </dl> <dl> <dt><a href="#">耳机<i>&gt;</i></a></dt> <dd> <a href="#">国企合作</a> <a href="#">国企合作</a> <a href="#">国企合作</a> </dd> </dl> <dl> <dt><a href="#">耳机<i>&gt;</i></a></dt> <dd> <a href="#">国企合作</a> <a href="#">国企合作</a> <a href="#">国企合作</a> </dd> </dl> </div> </div> </div> <script type="text/javascript" src='js/jquery.js'></script> <script type="text/javascript" src="utils.js"></script> <script type="text/javascript" src="index.js"></script> </body> </html> ~~~ index.js代码如下: ~~~ $(document).ready(function () { var sub = $('#sub') // 一級行 var activeRow // 二级菜单 var activeMenu // 设置延时 var timer = null; //判断是否在子菜单里 var mouseInSub = false; var mouseTrack = []; var mouseHandler = function (e) { mouseTrack.push({ x: e.pageX, y: e.pageY }) if (mouseTrack.length > 3) { mouseTrack.shift() } } sub.on('mouseenter', function (e) { mouseInSub = true; }).on('mouseleave', function (e) { mouseInSub = false; }) $('#test') .on('mouseenter',function (e) { sub.removeClass('none') $(document).bind('mousemove', mouseHandler) }) .on('mouseleave', function(e) { sub.addClass('none') if (activeRow) { activeRow.removeClass('active') activeRow = null } if (activeMenu) { activeMenu.removeClass('active') activeMenu = null } $(document).unbind('mousemove', mouseHandler) }) .on('mouseenter', 'li', function(e) { // 第一次赋值 if(!activeRow) { activeRow = $(e.target).addClass('active') activeMenu = $('#' + activeRow.data('id')) activeMenu.removeClass('none') return } if (timer) { clearTimeout(timer) } // 当前鼠标的位置 var currentMouse = mouseTrack[mouseTrack.length - 1] // 上一次鼠标的位置 var leftCorner = mouseTrack[mouseTrack.length -2] // 判断是否需要延时 // 如果鼠标移动的位置是在三角形里面,就需要延时 var delay = needDelay(sub, leftCorner, currentMouse) if (delay) { timer = setTimeout(function () { if (mouseInSub) { return } activeRow.removeClass('active') activeMenu.addClass('none') activeRow = $(e.target).addClass('active') activeMenu = $('#' + activeRow.data('id')) activeMenu.removeClass('none') timer = null }, 300) } else { var prevActiveRow = activeRow var prevActiveMenu = activeMenu activeRow = $(e.target) activeMenu = $('#' + activeRow.data('id')) prevActiveRow.removeClass('active') prevActiveMenu.addClass('none') activeMenu.removeClass('none') activeRow.addClass('active') } }) }) ~~~ utils.js代码如下: ~~~ // 根据向量来判断点是否在三角形内 function vector (a, b) { return { x: b.x - a.x, y: b.y - a.y } } function sameSign (a, b) { return (a ^ b) >= 0 } function vectorProduct (v1, v2) { return v1.x * v2.y - v1.y * v2.x } function isPointInTrangle (p, a, b, c) { var pa = vector(p, a) var pb = vector(p, b) var pc = vector(p, c) var t1 = vectorProduct(pa, pb) var t2 = vectorProduct(pb, pc) var t3 = vectorProduct(pc, pa) return sameSign(t1, t2) && sameSign(t2, t3) } function needDelay(elem, leftCorner, currentMouse) { var offset = elem.offset() var topLeft = { x: offset.left, y: offset.top } var bottomLeft = { x: offset.left, y: offset.top + elem.height() } return isPointInTrangle(currentMouse, leftCorner, topLeft, bottomLeft) } ~~~ 具体概念可以参考: [debounce 博客](http://www.cnblogs.com/fsjohnhuang/p/4147810.html) [课程视频学习](http://www.imooc.com/video/14716)