美文网首页让前端飞Web前端之路程序员
js模仿京东无延迟菜单效果

js模仿京东无延迟菜单效果

作者: 穆熙沐 | 来源:发表于2017-09-05 15:20 被阅读758次

    开发基本菜单结构,
    普通二级菜单效果,
    加入延迟解决移动问题,
    解决延迟引入的新问题

    效果图.png
    <style type="text/css">
        .wrap{
            position: relative;width: 200px;top: 50px;left: 50px
        }
        ul{
            padding: 15px 0;margin: 9;list-style: none;background: #6c6669;color: #ffffff; border-right-width: 0
        }
        li{
            display: block;height: 30px;line-height: 30px;padding-left: 12px;cursor: pointer;font-size: 14px;position: relative;
        }
        /*用类控制有利于JS增删类*/
    
        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);top: 0;left: 200px;box-sizing: border-box;margin: 0;padding: 10px;
        }
        .sub_content a{
            font-size: 12px;color: #666;text-decoration: none;
        }
        .sub_content dd a{
            border-left: 1px solid #e0e0e0;padding: 0 10px;margin: 4px 0;
        }
        /*清除浮动 BFC*/
        .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 consolas;position: absolute; right: 5px;top: 5px;
        }
    </style>
    
    <div class="wrap" 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="e">
                <span>男装 / 女装 / 童装 / 内衣</span>
            </li>
        </ul>
        <div id="sub" class="none">
            <div id="a" class="sub_content none">
                <dl>
                    <dt>
                        <a href="#">电视<i>></i></a>
                    </dt>
                    <dd>
                        <a href="#">合资品牌</a>
                        <a href="#">国产品牌</a>
                        <a href="#">互联网品牌</a>
                    </dd>
                </dl>
                <dl>
                    <dt>
                        <a href="#">空调<i>></i></a>
                    </dt>
                    <dd>
                        <a href="#">中央空调</a>
                        <a href="#">格式空调</a>
                        <a href="#">挂式空调</a>
                    </dd>
                </dl>
                <dl>
                    <dt>
                        <a href="#">洗衣机<i>></i></a>
                    </dt>
                    <dd>
                        <a href="#">波轮洗衣机</a>
                        <a href="#">烘托一体机</a>
                    </dd>
                </dl>
                <dl>
                    <dt>
                        <a href="#">冰箱<i>></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>></i></a>
                    </dt>
                    <dd>
                        <a href="#">手机</a>
                        <a href="#">对讲机</a>
                        <a href="#">以旧换新</a>
                    </dd>
                </dl>
                <dl>
                    <dt>
                        <a href="#">手机配件<i>></i></a>
                    </dt>
                    <dd>
                        <a href="#">合资品牌</a>
                        <a href="#">国产品牌</a>
                        <a href="#">互联网品牌</a>
                    </dd>
                </dl>
                <dl>
                    <dt>
                        <a href="#">运营商<i>></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>></i></a>
                    </dt>
                    <dd>
                        <a href="#">笔记本</a>
                        <a href="#">游戏本</a>
                        <a href="#">平板电脑</a>
                    </dd>
                </dl>
                <dl>
                    <dt>
                        <a href="#">电脑配件<i>></i></a>
                    </dt>
                    <dd>
                        <a href="#">显示器</a>
                        <a href="#">CPU</a>
                        <a href="#">主板</a>
                    </dd>
                </dl>
                <dl>
                    <dt>
                        <a href="#">外设产品<i>></i></a>
                    </dt>
                    <dd>
                        <a href="#">鼠标</a>
                        <a href="#">键盘</a>
                    </dd>
                </dl>
            </div>
            <div id="d" class="sub_content none">
                <dl>
                    <dt>
                        <a href="#">厨具<i>></i></a>
                    </dt>
                    <dd>
                        <a href="#">烹饪锅具</a>
                        <a href="#">保温杯</a>
                        <a href="#">菜板刀具</a>
                    </dd>
                </dl>
                <dl>
                    <dt>
                        <a href="#">家纺<i>></i></a>
                    </dt>
                    <dd>
                        <a href="#">被子</a>
                        <a href="#">枕芯</a>
                        <a href="#">蚊帐</a>
                    </dd>
                </dl>
                <dl>
                    <dt>
                        <a href="#">生活日用<i>></i></a>
                    </dt>
                    <dd>
                        <a href="#">收纳用品</a>
                        <a href="#">雨伞雨具</a>
                    </dd>
                </dl>
            </div>          
            <div id="e" class="sub_content none">
                <dl>
                    <dt>
                        <a href="#">女装<i>></i></a>
                    </dt>
                    <dd>
                        <a href="#">商场同款</a>
                        <a href="#">当即热卖</a>
                        <a href="#">2017新品</a>
                    </dd>
                </dl>
                <dl>
                    <dt>
                        <a href="#">男装<i>></i></a>
                    </dt>
                    <dd>
                        <a href="#">牛仔裤</a>
                        <a href="#">休闲裤</a>
                        <a href="#">衬衫</a>
                    </dd>
                </dl>
                <dl>
                    <dt>
                        <a href="#">内衣<i>></i></a>
                    </dt>
                    <dd>
                        <a href="#">大码内衣</a>
                        <a href="#">打底衫</a>
                    </dd>
                </dl>
            </div>  
        </div>
    </div>
    <script type="text/javascript" src="jquery.min.js"></script>
    <script type="text/javascript" src='function.js'></script>
    <script type="text/javascript">
        // 事件代理方式进行绑定mouseenter/mouseover的区别:
        // mouseover和mouseout,如果鼠标移动到子元素上,即便没有离开父元素,也会触发父元素的mouseout事件,mouseenter/mouseleave,如果鼠标没有离开父元素,在子元素上任意移动,也不会触发mouseleave事件
        $(document).ready(function(){
            var sub = $("#sub");
            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()
                }
            }
            $('#test')
                .on('mouseenter',function(e){
                    sub.removeClass('none');//二级菜单显示
                    $(document).on('mousemove',moveHandler)
                })
                .on('mouseleave',function(e){
                    sub.addClass('none');//二级菜单隐藏
                    // console.log(activeRow)
                    if (activeRow) {
                        activeRow.removeClass('active');
                        activeRow = null;
                    }
                    if(activeMenu){
                        activeMenu.addClass('none');
                        activeMenu = null;
                    }
                    $(document).unbind('mousemove',moveHandler)//解绑mousemove
                })
                .on('mouseenter','li',function(e){//事件代理方式绑定
    
                    if (!activeRow) {
                        activeRow = $(e.target).addClass('active');
                        activeMenu = $('#'+activeRow.data('id'));
                        activeMenu.removeClass('none');
                        return
                    }
                    // 加入延迟优化
                // 切换子菜单的时候,用setTimeout设置延迟。(但是不能迅速切换)
                // debounce去抖技术,在事件频繁触发时,只执行一次处理,最后一次(计时器还没执行的时候,清除计时器,并在计时器设置空)
                    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
                            }
                            //li之间切换,清除之前状态
                            activeRow.removeClass('active');
                            activeMenu.addClass('none');
                            // 指向当前li
                            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>
    

    function.js封装的函数:

        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(pa,pc)
        var t3 = vectorProduct(pb,pc)
    
        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)
    

    }

    相关文章

      网友评论

        本文标题:js模仿京东无延迟菜单效果

        本文链接:https://www.haomeiwen.com/subject/nviujxtx.html