隔離 鼠標點擊 雙擊 移動事件
應用場景
在編寫一個3d卡片切換的時候,需要執行
1. **單擊** 觸發模態框
2. **雙擊** 卡片定位
3. **拖動** 卡片跟隨
問題
默認dom事件
鼠標**雙擊**的時候會觸發**單擊**
鼠標按下**拖動**的開始會觸發**單擊**
因此需要分離這三種事件 雙擊無法觸發單擊 拖動時無法觸發單擊
解決方案
思路
2.實現點擊事件
分析:需要的點擊事件 單擊 雙擊 拖拽(移動)
通過mousedown , mouseup , mousemove ,mouseleave控制
2.0 當鼠標事件
按下時 mousedown
註冊mousemove , mouseup 和 mouseleave
擡起時 mouseup
註銷mousemove , mouseup 和 mouseleave, 判斷事件類型<單擊,雙擊,已觸發的移動> 並且 清空所有按鍵信息
離開時 mouseleavel
註銷mousemove , mouseup 和 mouseleave 並且 清空所有按鍵信息
2.1 單擊 mouseup事件下
當鼠標不是在移動(mouseCtrl.ismoving爲false)且
當鼠標按下的和擡起的間隔小於一個值默認150毫秒 mouseDiffTime < 150 即爲一次有效點擊
開啓一個異步延遲的單擊事件 延遲事件 默認爲300毫秒 將事件id緩存下來
2.2 雙擊 mouseup事件下
觸發同樣的單擊事件判斷
如果存在事件id則代表上一次單擊還未執行, (那即這是150毫秒+300毫秒 之間的第二此點擊 即爲雙擊)
取消事件id的執行(取消單擊事件)
觸發雙擊事件
2.3 移動 mousemove 事件下
判斷 鼠標按下的初始位置和當前位置的x距離如果(mouse.moveX) 大於 20
觸發移動事件
實現代碼
第一部分爲事件系統提供一個 註冊 銷燬 觸發 的函數
/**一個事件註冊函數
* @constructor
*/
function RegisterEvents() {
/** 從數組中移除某個元素
* @param {[]} arr
* @param {*} target
*/
var removeItemFromArr = function (arr , target) {
for (var i = 0; i < arr.length; i++) {
var element = arr[i];
if(element === target) return arr.splice(i,1)[0];
}
return null;
};
var eventsStore = {
"click":[],
"dblclick":[],
"movingEvent":[]
};
/** 註冊事件函數
* @param {'click'| 'dblclick' | 'movingEvent'} type
* @param {Function} fnc params <{}>
*/
this.on = function (type,fnc) {
if(eventsStore[type] === undefined) throw new Error("綁定的事件類型不存在");
if(typeof fnc !== 'function') throw new Error("綁定的事件不是可執行函數");
eventsStore[type].push(fnc);
};
/** 移除事件函數
* @param {'click'| 'dblclick' | 'movingEvent'} type
* @param {Function} fnc
*/
this.off = function (type,fnc) {
if(type && eventsStore[type] === undefined) throw new Error("綁定的事件類型不存在");
if(type === undefined){
eventsStore = [];
}else if(typeof fnc !== 'function'){
eventsStore[type].length = 0;
}else{
removeItemFromArr(eventsStore[type],fnc);
}
};
/** 移除事件函數
* @param {'click'| 'dblclick' | 'movingEvent'} type
* @param {{event:EventTarget}} info ?
*/
this.emit = function (type,info) {
if(eventsStore[type] === undefined) throw new Error("綁定的事件類型不存在");
eventsStore[type].forEach(e=>e(info));
};
}
第二部分 主體實現分離
/**提供事件系統 增加 單擊 雙擊 移動 事件
* @constructor
* @param {JQuery<HTMLElement> } dom
* @extends RegisterEvents
*/
function EventSystem(dom) {
/**保存當前實例 */
var instance = this;
if(!dom.jquery) dom = $(dom);
var mouseCtrl = generateMouseCtrl();
/**鼠標按下時 註冊鼠標移動函數 */
dom.on('mousedown', function (e) {
mouseCtrl.isdown = true;
mouseCtrl.event = e;
registerDownAfterListen();
mouseCtrl.mouseDownTime = new Date();
mouseCtrl.moveStartX = e.screenX;
console.log('mousedown');
});
/**在點擊之後需要開啓的所有監聽 */
function registerDownAfterListen() {
dom.on('mousemove',mousemove);
dom.on('mouseup',mouseup);
dom.on('mouseleave',mouseleave);
}
/**註銷所有因爲Donw而註冊的監聽 */
function cancelDownAfterListen() {
dom.off('mousemove',mousemove);
dom.off('mouseup',mouseup);
dom.off('mouseleave',mouseleave);
}
function mousemove(e) {
/**因爲只有在mousedown的時候纔會註冊mousemove事件 所以無須 檢測是否按下 */
// if(mouseCtrl.isdown===false) return ;
/**當前位置和上一次位置的差值 */
var constantlyOffset = e.screenX - (mouseCtrl.moveEndX || mouseCtrl.moveStartX);
mouseCtrl.moveEndX = e.screenX;
if(Math.abs(mouseCtrl.moveX)>20 && !mouseCtrl.ismoving){
mouseCtrl.ismoving = true;
}
if(mouseCtrl.ismoving){
instance.emit('movingEvent',{
event:mouseCtrl.event,
moveX:mouseCtrl.moveX,
constantlyOffset:constantlyOffset
});
}
console.log('mousemove');
}
/**@TODO:
* 1.將點擊事件也集成到mouseup上
* 1.1 當mouseCtrl.ismoving爲false且
* 1.2 當mouseDiffTime < 150 即爲一次有效點擊
* 1.3 如果不存在 clickId 開啓一個延時點擊函數 clickId = excuteClick():async;
* 1.4 如果存在 clickId 取消 clickId:async 執行 excuteDbclick();
* */
function mouseup() {
mouseCtrl.mouseUpTime = new Date();
if(!mouseCtrl.ismoving && mouseCtrl.mouseDiffTime < mouseCtrl.mouseDiffTimeMax){
mouseCtrl.clickId ? dblclick():singleClick();
}else{
/**這裏如果觸發了單擊或者雙擊 因爲 單擊時有延時的 所以不應該直接釋放mouseCtrl */
mouseCtrl.isreleased = true;
}
cancelDownAfterListen();
console.log('mouseup');
}
function singleClick() {
mouseCtrl.clickId = setTimeout(function () {
instance.emit('click',{event:mouseCtrl.event});
mouseCtrl.isreleased = true;
console.log('singleClick');
}, mouseCtrl.mouseDoubleClickTimeMax);
}
function dblclick() {
if(mouseCtrl.clickId) clearTimeout(mouseCtrl.clickId);
instance.emit('dblclick',{event:mouseCtrl.event});
mouseCtrl.isreleased = true;
console.log('dblclick');
}
function mouseleave() {
cancelDownAfterListen();
mouseCtrl.isreleased = true;
}
this.destroy = function () {
this.off();
try {
cancelDownAfterListen();
} catch(e){console.log(e);}
dom.off('mousedown');
dom = null;
mouseCtrl = null;
};
/**生成鼠標屬性管理
* @returns {{isdown:false,ismoving:false,isreleased:true,moveX:null,clickId:null,moveStartX,moveEndX,mouseDownTime,mouseUpTime,mouseDiffTime,mouseDiffTimeMax:150,mouseDoubleClickTimeMax:300}} 當鼠標釋放的時候會重置所有 isdown:false ismoving:false moveX:null clickId:null
* @summary isdown 基本無用
*/
function generateMouseCtrl() {
/**是否按下,是否移動,是否釋放,鼠標移動的橫向距離起始點,鼠標橫向移動的重點 鼠標按下的初始時間 鼠標擡起的時間 */
var isdown , ismoving , isreleased,moveStartX,moveEndX ,mouseDownTime , mouseUpTime ;
var mouseCtrl = {
/**觸發事件的id 單擊 */
clickId:null,
/**鼠標點擊觸發的window.event */
event:null,
/**鼠標橫向移動的距離 */
moveX:null,
/**鼠標點擊擡起的間隔時間 */
mouseDiffTime:null,
/**固定值 鼠標按下擡起是否時單擊的時間最大值 */
mouseDiffTimeMax:150,
/**固定值 兩次鼠標擡起的最大時間間隔, 在其之內 則爲雙擊 在其之位則爲兩次單擊 */
mouseDoubleClickTimeMax:300
};
function init() {
isdown = false;
ismoving = false;
mouseCtrl.clickId = null;
mouseCtrl.moveX = null;
moveStartX = null;
moveEndX = null;
mouseDownTime = null;
mouseUpTime = null;
mouseCtrl.mouseDiffTime = null;
mouseCtrl.event = null;
}
Object.defineProperty(mouseCtrl,'isdown',{
get:function () {
return isdown;
},
/** @param {Boolean} bol */
set:function (bol) {
isdown = bol;
isreleased = !bol;
}
});
Object.defineProperty(mouseCtrl,'isreleased',{
get:function () {
return isreleased;
},
/** @param {Boolean} bol */
set:function (bol) {
// if(bol === isreleased) return ;
isreleased = bol;
if(bol) {
init();
}
}
});
Object.defineProperty(mouseCtrl,'ismoving',{
get:function () {
return ismoving;
},
/** @param {Boolean} bol */
set:function (bol) {
if(isdown) {
ismoving = bol;
}else {
ismoving = false;
}
}
});
Object.defineProperty(mouseCtrl,'moveStartX',{
get:function () {
return moveStartX;
},
/** @param {number} startX */
set:function (startX) {
moveStartX = startX;
moveEndX = 0;
}
});
Object.defineProperty(mouseCtrl,'moveEndX',{
get:function () {
return moveEndX;
},
/** @param {number} endX */
set:function (endX) {
if(typeof moveStartX !=="number") throw new Error("開始位置moveStartX必須爲數字類型");
moveEndX = endX;
mouseCtrl.moveX = moveEndX - moveStartX;
}
});
Object.defineProperty(mouseCtrl,'mouseDownTime',{
get:function () {
return mouseDownTime;
},
/** @param {number} mouseDownTime */
set:function (downTime) {
if((typeof downTime ==="number") || !( downTime instanceof Date)) throw new Error("downTime必須爲數字類型或者時間類型");
mouseDownTime = downTime;
}
});
Object.defineProperty(mouseCtrl,'mouseUpTime',{
get:function () {
return mouseUpTime;
},
/** @param {number} endX */
set:function (upTime) {
if((typeof upTime ==="number") || !( upTime instanceof Date)) throw new Error("upTime必須爲數字類型或者時間類型");
mouseUpTime = upTime;
mouseCtrl.mouseDiffTime = mouseUpTime - mouseDownTime;
}
});
mouseCtrl.isreleased = true;
return mouseCtrl;
}
}
EventSystem.prototype = new RegisterEvents();
EventSystem 提供
1. EventSystem.on(type,callback)註冊函數 ‘click’| ‘dblclick’ | ‘movingEvent’ 可選值
2. EventSystem.off(type,callback)註銷函數
3. EventSystem.emit (type,value) 觸發函數
4. EventSystem.destroy() 銷燬函數
總結
啊啊啊啊,可以去掉jquery。