JS實現京東無延遲菜單效果

          閒暇之餘看了慕課網 JS實現京東無延遲菜單效果(附慕課網視頻連接: https://www.imooc.com/learn/829)

    後看到了一位前輩的播客(附原播客地址:http://www.cnblogs.com/6kou/p/jd.html)

         輾轉反側,狂翻百度 

         後研究算法,終理解其中緣由

        話不多說,先附前輩代碼並稍加整合(侵刪):

一:原理

    1.開發基本的菜單結構     
    2.開發普通的二級菜單效果

    3.假如延遲解決移動問題 

            切換子菜單時候,用setTimeout設置延遲;debounce去抖技 ;在事件被頻繁觸發是,只執行一次處理;

    4.解決延遲引入的新問題  

  跟蹤鼠標的移動
  用鼠標當前位置,和鼠標上一次位置與子菜單上下邊緣的三角形區域進行比較
  
  運用到向量
  二位向量叉乘公式

  用叉乘法判斷點在三角形內

(附算法鏈接:https://baike.baidu.com/item/%E5%90%91%E9%87%8F/1396519?fr=aladdin

 算法: https://www.cnblogs.com/TenosDoIt/p/4024413.html)

一:HTML

    

<!doctype html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>仿京東菜單無刷新導航欄</title>

    <style>
        .wrap{
            position:relative;
            width:200px;
            left:50px;
            top:50px;
        }

        ul{
            padding:15px;
            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;
        }

        /*鼠標移動上去的背景色*/
        li.active{
            background:#999395;
        }
    
        /*js可以很好地調用類,一般效果css實現就好*/
        li span:hover{
            color:#c81623;
        }

        /*隱藏的類*/
        .none{
            display: none;
        }

        /*二級菜單*/
        #sub{
            width:600px;
            position: absolute;
            border:1px solid #f7f7f7;
            background:#f7f7f7;
            box-shadow:2px 0 rgba(0,0,0,.3);
            left: 200px;
            top:0;
            box-sizing:border-box;
            margin: 0px;
            padding:10px;
        }

        .sub-content a{
            font-style:12px;
            color:#666;
            text-decoration:none;
        }

        .sub-content dd a{
            border-left:1px solid #e0e0e0;
            padding:0 1px;
            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 consolas;
            position: absolute;
            right:5px;
            top:5px;
        }
    </style>
</head>
<body>
<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>
    <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>
</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="#">HDR電視</a>
                    <a href="#">DLED電視</a>
                </dd>
                <dt>
                    <a href="#">空調<i>></i></a>
                </dt>
                <dd>
                    <a href="#">掛壁式空調</a>
                    <a href="#">櫃式空調</a>
                    <a href="#">中央空調</a>
                    <a href="#">以舊換新</a>
                </dd>
                <dt>
                    <a href="#">洗衣機<i>></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>></i></a>
                </dt>
                <dd>
                    <a href="#">手機</a>
                    <a href="#">對講機</a>
                    <a href="#">以舊換新</a>
                    <a href="#">手機維修</a>
                </dd>
                <dt>
                    <a href="#">運營商<i>></i></a>
                </dt>
                <dd>
                    <a href="#">合約機</a>
                    <a href="#">選號機</a>
                    <a href="#">固定電話</a>
                    <a href="#">辦寬帶</a>
                </dd>
                <dt>
                    <a href="#">手機配件<i>></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>></i></a>
                </dt>
                <dd>
                    <a href="#">筆記本</a>
                    <a href="#">遊戲本</a>
                    <a href="#">平板電腦</a>
                </dd>
                <dt>
                    <a href="#">電腦配件<i>></i></a>
                </dt>
                <dd>
                    <a href="#">顯示器</a>
                    <a href="#">CPU</a>
                    <a href="#">主板</a>
                </dd>
                <dt>
                    <a href="#">外設產品<i>></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>></i></a>
                </dt>
                <dd>
                    <a href="#">烹飪鍋具</a>
                    <a href="#">刀剪配件</a>
                    <a href="#">廚房配件</a>
                    <a href="#">水具酒具</a>
                </dd>
                <dt>
                    <a href="#">家紡<i>></i></a>
                </dt>
                <dd>
                    <a href="#">牀品套件</a>
                    <a href="#">被子</a>
                    <a href="#">枕芯</a>
                    <a href="#">蚊帳</a>
                </dd>
                <dt>
                    <a href="#">生活日用<i>></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>></i></a>
                </dt>
                <dd>
                    <a href="#">商城同款</a>
                    <a href="#">當季熱賣</a>
                    <a href="#">2017新品</a>
                    <a href="#">連衣裙</a>
                </dd>
                <dt>
                    <a href="#">男裝<i>></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>></i></a>
                </dt>
                <dd>
                    <a href="#">補水保溼</a>
                    <a href="#">卸妝</a>
                    <a href="#">潔面</a>
                </dd>
            </dl>
        </div>
    </div>
</ul>
<script type="text/javascript" src="jquery-2.2.4.min.js"></script>
<script type="text/javascript" src="index.js"></script>
<script type="text/javascript" src="suanfa.js"></script>
</body>
</html>

二:JS( 內附一些需要說明 )

// index.js 
$(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).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')
            }
        })
})
// suanfa.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(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)
}

最後算法詳解說明:

        

    寫不懂可以把實際座標點帶進去

    說一下畫這個圖的思想  

        JD 側導航欄  A的X軸可以想成移入的li標籤,BC線可以想成二級菜單欄  從上下移入都可以  轉換成響應的圖就可以了

    

    我也不知道爲什麼學開發的一個人突然想起來手寫,還這麼難看   

    如有不懂留言交流,抑或其它。。

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章