閒暇之餘看了慕課網 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.jsfunction 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線可以想成二級菜單欄 從上下移入都可以 轉換成響應的圖就可以了
我也不知道爲什麼學開發的一個人突然想起來手寫,還這麼難看
如有不懂留言交流,抑或其它。。