# 京東無延時菜單效果
主要知識點:
* 使用向量來判斷點是否在三角形內
* 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>></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>
</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>
</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>
</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 id="g" 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>
</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)
- 前端
- pc端
- 京東無延時菜單效果
- datapicker組件的編寫
- cursor禁止默認樣式
- 移動端
- web在真機調試
- 有關vue的上拉加載
- 下拉刷新
- vue獲取dom節點
- worker技術
- h5頁面喚醒手機撥號,發短信功能
- 彈出層喚醒擠壓css背景圖片
- 23模式
- 基礎模式
- SMD同步模塊
- canvas
- 點的均勻分布
- 線條繪畫
- canvas常見的全局屬性
- 判斷點在某個區間內
- 保證動畫效果的流暢性
- arc函數學習
- cookies
- 本地服務搭建
- Mint UI框架的學習
- 第一天
- Swiper組件的解析
- loadmore組件
- 二維碼的生成
- 前端優化
- 瀏覽器緩存
- indexdb本地數據庫的使用
- 淺談前端優化
- css漸變屬性
- 前端常用組件學習
- picker組件
- vue
- 模塊下的state訪問
- vue的進階
- npm包的發布
- 淘寶移動端適配問題
- vue的生命周期的理解
- vue的路由學習
- vue自帶的動畫效果
- vue 項目里,img標簽報錯,添加默認圖片
- axios 實現loading加載提示
- qs庫
- vue的v-for指令無法使用在tr、td中
- vue打包之后,npm run dev 空白
- vue 單頁面百度統計
- unix時間轉化
- UI組件的實現
- picker
- upload組件
- js動畫效果
- canvas甜甜圈的編寫
- 瀑布流實現
- 適配問題
- 圖片加載
- js基礎知識
- js數組合并
- 響應布局總結
- 組件開發
- grid組件
- 專題頁面常用布局頁面
- scroll滾動問題
- 項目整理經驗收集
- 畫圖工具應用實現記錄
- 參考線實現
- JCode
- 多條件篩選不用ajax的原因分析
- node
- koa路由下的分塊
- 連接數據庫mysql
- 初步搭建數據庫模型
- java
- spring框架的學習
- maven 搭建web項目
- 建站
- 關聯github
- 雜文
- 后端常用成熟系統
- node實戰練習
- 編寫郵箱通知
- 微信小程序
- 模板之坑
- 模板坑之數據傳入
- 小程序組件化之一
- 有關this的綁定
- 小程序組件化之二
- 小程序分包加載
- tab切換,頁面狀態不更新
- 小程序請求組件(草稿)
- php學習之路
- 文件上傳
- 有關數據庫時間類型的插入
- Ubuntu下的php開啟擴展模塊
- windows系統下的hosts文件位置
- 騰訊云的cos
- thinkPHP
- 模塊設計
- 模型的使用
- ajax的請求異常總結
- apache服務器
- 虛擬主機的建立
- ssh的使用
- ftp服務器配置
- Lamp的搭建
- ftp從零開始配置
- https的配置
- git hooks實現簡單的自動部署
- ubuntu系統登錄遠程ubuntu服務器
- ubuntu
- 新建用戶需要使用sudo
- sftp的文件上傳問題
- 配置apache部分問題總結
- git服務器的搭建
- githook的學習
- ubuntu 虛擬主機搭建
- ubunut的服務器的防火墻的關閉
- 寶塔部署laravel
- 關于寶塔phpmyadmin無法訪問的問題
- 解決碼云每次提交輸入密碼的問題
- mysql
- phpmyadmin的安裝
- 遠程登陸數據庫
- git的使用
- 拉去分支的代碼
- git remote指令
- 學習臨時筆記
- ios兼容性問題
- 蘋果獲取屏幕寬度問題
- toDataURL無法導出圖片
- 蘋果觸屏滾動,下拉刷新問題
- jquery動畫
- jquery的animate無法使用transform
- jquery隊列queue
- css重學之路
- 規范一
- css3的matrix屬性
- 淘寶適配方案
- Yii框架學習
- gd庫的支持安裝
- larval框架學習
- 中間件
- 指令
- 自定義指令生成token
- 微信登錄后端篇
- 寶塔apache配置ssl
- SVN
- PS
- 快捷鍵
- Redis
- Redis在Unbtun下的配置
- 微信公眾號開發
- 測試號配置