js实现无延迟菜单学习笔记

 

慕课网学习无延迟菜单效果:https://www.imooc.com/learn/829

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>无延迟菜单效果</title>
    <style>
        .warp {
            position: relative;
            width: 200px;
            left: 50px;
            top: 50px;
        }
        ul {
            padding: 15px 0 ;
            margin: 9px;
            list-style: none;
            background: #6c6669;
            color: #fff;
            border-right-width: 11px;
        }
        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,.3);
            left: 200px;
            top: 0;
            box-sizing: border-box;
            padding: 10px;
        }
        .sub_content a {
            font-size: 12px;
            color: #666666;
            text-decoration: none;
        }
        .sub_content dd a {
            border-left:1px solid #e0e0e0;
            padding: 0 10px;
            margin: 4px 0;
        }
        .sub_content dl {
            overflow: hidden;
        }
        .sub_content dt {
            float: left;
            width: 70px;
            font-weight: bold;
            clear: left;
            position: relative;
        }
        .sub_content dd {
            float:left;
            margin-left: 5px;
            border-top: 1px solid #eee;
            margin-bottom: 5px;
        }
        .sub_content dt i {
            width: 4px;
            height: 14px;
            font:400 9px/14px contextual;
            position: absolute;
            right: 5px;
            top: 5px;
        }
    </style>
</head>
<body>
<div class="warp" id="warp">
    <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="e">
            <span>男装/女装/童装/内衣</span>
        </li>
        <li data-id="f">
            <span>个护化妆/清洁用品/宠物</span>
        </li>
        <li data-id="g">
            <span>箱包</span>
        </li>
        <li data-id="h">
            <span>运动</span>
        </li>
        <li data-id="i">
            <span>汽车</span>
        </li>
        <li data-id="j">
            <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="#">HDR电视</a>
                    <a href="#">DLED电视</a>
                </dd>
                <dt>
                    <a href="#">空调<i>&gt</i></a>
                </dt>
                <dd>
                    <a href="#">挂壁式空调</a>
                    <a href="#">柜式空调</a>
                    <a href="#">中央空调</a>
                    <a href="#">以旧换新</a>
                </dd>
                <dt>
                    <a href="#">洗衣机<i>&gt</i></a>
                </dt>
                <dd>
                    <a href="#">滚筒式洗衣机</a>
                    <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>
                    <a href="#">手机维修</a>
                </dd>
                <dt>
                    <a href="#">运营商<i>&gt</i></a>
                </dt>
                <dd>
                    <a href="#">合约机</a>
                    <a href="#">选号机</a>
                    <a href="#">固定电话</a>
                    <a href="#">办宽带</a>
                </dd>
                <dt>
                    <a href="#">手机配件<i>&gt</i></a>
                </dt>
                <dd>
                    <a href="#">手机壳</a>
                    <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>
                <dt>
                    <a href="#">电脑配件<i>&gt</i></a>
                </dt>
                <dd>
                    <a href="#">显示器</a>
                    <a href="#">CPU</a>
                    <a href="#">主板</a>
                </dd>
                <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>
                    <a href="#">水具酒具</a>
                </dd>
                <dt>
                    <a href="#">家纺<i>&gt</i></a>
                </dt>
                <dd>
                    <a href="#">床品套件</a>
                    <a href="#">被子</a>
                    <a href="#">枕芯</a>
                    <a href="#">蚊帐</a>
                </dd>
                <dt>
                    <a href="#">生活日用<i>&gt</i></a>
                </dt>
                <dd>
                    <a href="#">收纳用品</a>
                    <a href="#">雨伞雨具</a>
                    <a href="#">净化除味</a>
                    <a href="#">浴室用品</a>
                </dd>
            </dl>
        </div>
        <div id="e" class="sub-content none">
            <dl>
                <dt>
                    <a href="#">女装<i>&gt</i></a>
                </dt>
                <dd>
                    <a href="#">商城同款</a>
                    <a href="#">当季热卖</a>
                    <a href="#">2017新品</a>
                    <a href="#">连衣裙</a>
                </dd>
                <dt>
                    <a href="#">男装<i>&gt</i></a>
                </dt>
                <dd>
                    <a href="#">商城同款</a>
                    <a href="#">当季热卖</a>
                    <a href="#">2017新品</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>
</div>
</body>
<script src="http://code.jquery.com/jquery-1.12.4.min.js"></script>
<script>
    //事件代理的方式进行绑定
    //    MouseEnter 和 MouseOver的区别
    //        使用ouseover/mouseout时,如果鼠标移动到子元素上,即便没有离开父元素,也会触发父元素的mouseout事件
    //使用mouseenter/mouseleave时,如果鼠标没有离开父元素,在其子元素上任意移动,也不会触发mouseleave事件

    //    debounce去抖技术  当事件被频繁触发的时候只执行一次处理

    // 一级菜单 warp 二级菜单 sub
    $(document).ready(function(){
        var sub = $('#sub');
        var warp = $('#warp');
//        一级菜单 二级菜单当前的位置
        var activeRow;
        var activeMenu;
        //计时器
        var timer;
        //鼠标是否在二级菜单上
        var mouseInSub = false;

        sub.on('mouseenter',function(e){
            mouseInSub = true;
        }).on('mouseleave',function(e){
            mouseInSub = false;
        })
        //记录鼠标的位置
        var mouseTrack = []

        var moveHandler = function(e){
            mouseTrack.push({
                x:e.pageX,
                y:e.pageY
            })

            if(mouseTrack.length > 3){
                mouseTrack.shift();
            }
        }

        warp
            .on('mouseenter',function(e){
                sub.removeClass('none');
                $(document).bind('mousemove',moveHandler);
            })
            .on('mouseleave',function(e){
                sub.addClass('none');
                if(activeRow){
                    activeRow.removeClass('active');
                    activeRow = null;
                }

                if(activeMenu){
                    activeMenu.addClass('none');
                    activeMenu = null;
                }
                $(document).unbind('mousemove',moveHandler);
            })

            .on('mouseenter','li',function(e){
                if(!activeRow){
                    activeRow = $(e.target).addClass('acrive');
                    activeMenu = $('#'+activeRow.data('id'));
                    activeMenu.removeClass('none');
                    return
                }
                if(timer){
                    clearTimeout(timer)
                }
                //鼠标当前坐标
                var  currMousePos = mouseTrack[mouseTrack.length - 1];
                //上次的坐标
                var leftCorner = mouseTrack[mouseTrack.length-2];
                var delay = needDelay(sub,leftCorner,currMousePos);

                if(delay){
                    timer = setTimeout(function(){
                        //判断
                        if(mouseInSub){
                            return
                        }
                        activeRow.removeClass('active');
                        activeMenu.addClass('none');
                        activeRow = $(e.target);
                        activeRow.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');
                    activeRow.addClass('active');
                    activeMenu.removeClass('none');
                }
            })
    })
</script>
<script>
   /*
   *   基于用户的行为预测的切换技术
        跟踪鼠标的移动
        鼠标当前位置和鼠标上一次位置与子菜单上下边缘形成的三角行区域进行比较
   *
   *    如何比较:
   *        向量:Vab  = Pb - Pa
   *        二维向量叉乘公式:
   *        a(x1,y1) * b(x2,y2) = x1*y2 - x2*y1
   *        用叉乘法判断点在三角形内
   *        向量的定义 终点的坐标减去起点的坐标
   * */
   function sameSign(a,b){
       return (a ^ b) >= 0
   }
   function vector(a,b){
       return{
           x:b.x - a.x,
           y:b.y - a.y
       }
   }

   function vectorProduct(v1,v2){
       return v1.x * v2.y - v2.x * v1.y
   }

   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,currMousePos){
       var offset = elem.offset()

       var topLeft = {
           x:offset.left,
           y:offset.top
       }

       var bottomLeft = {
           x:offset.left,
           y:offset.top + elem.height()
       }

       return isPointInTrangle(currMousePos,leftCorner,topLeft,bottomLeft)
   }
</script>
</html>

 

posted @ 2017-12-09 16:56  牛三  阅读(400)  评论(0)    收藏  举报