javascript
sortable 拖拽时互换目标的位置_双端通用型JS拖拽插件的封装与应用
最近工作中遇到一個需求,需要將一個元素從某位置拖動到另一固定位置后執行某一交互行為,具體效果如下:
這個看似簡單的需求,然而實現起來卻并不那么順利。我首先想到的是如何通過哪個現有的插件來快速解決這個問題,然而找了半天,并未找到合適的原生js插件,總會在實際使用當中出現一些莫名其妙的問題。所以,一不做二不休,干脆自己封裝一個得了!一方面以后可能還會遇到類似的需求,另一方面自己寫的總歸更加熟悉,日后也更好維護和拓展。
閑言少敘,接下來就讓我們一步步用原生js來實現這個簡單的拖拽插件。
一、“類”的構建
插件,當然得有插件的樣子。這里我用的是ES6中的class語法糖來實現“類”的封裝,這樣我們之后在使用插件時只需new一個對象就可以了。
// 定義拖拽插件class Drag{ constructor(selector, options){ }}// 使用拖拽插件new Drag('.box'); // 該box元素可拖拽二、獲取元素
既然是拖拽插件,當然首先得獲取需要被拖拽的元素,這里我們可以讓插件使用者直接把已經獲取到的元素對象傳進來,也可以以CSS選擇器的方式傳進來,在插件內部進行元素獲取。
具體可以通過類型判斷來實現,代碼如下:
getElement(selector){ if(typeof selector === 'string'){ // 傳入css選擇器 return document.querySelector(selector); } else if(typeof selector === 'object'){ // 傳入DOM對象 return selector; } else { throw '請傳入正確的元素'; }}然后我們可以在constructor中調用它:
constructor(selector, options){ this.el = this.getElement(selector); if(!this.el){ throw `未找到移動元素`; }}三、通過事件對象獲取鼠標(或手指)位置
在處理拖拽事件之前,我們得先知道拖拽的基本原理是什么。
拖拽,本質上是鼠標(或手指)在元素上按下后,移動鼠標(或手指)時元素跟隨鼠標指針(或手指)位置移動,最后當鼠標(或手指)松開時元素停止移動。
其中最關鍵的部分就是鼠標(或手指)移動時,鼠標(或手指)位置的獲取,這時我們就要用到?事件對象?了。
1. PC端獲取鼠標位置
e.clientX // 橫坐標e.clientY // 縱坐標2. 移動端獲取手指位置
這里又得分兩種情況,一種是手指移動時,一種是手指松開時。
① 手指移動時,也就是touchmove事件
e.touches[0].clientX // 橫坐標e.touches[0].clientY // 縱坐標為什么是touches[0]呢?因為我們只用到了一根手指呀!
② 手指松開時,也就是touchend事件
e.changedTouches[0].clientX // 橫坐標e.changedTouches[0].clientY // 縱坐標看到了嗎?無論是哪種方式,我們獲取鼠標(或手指)的位置都是clientX和clientY,只不過前面的那個對象不一樣而已。這時為了代碼良好的復用性,我們可以對前面的那個對象進行簡單的封裝。
eventObj(event,isEnd = false){ // isEnd代表是否是手指松開時 return isMobile() ? (isEnd ? event.changedTouches[0] : event.touches[0]) : event;}// 判斷是否是移動端,因為移動端才會有ontouchstartfunction isMobile() { return document.body.ontouchstart;}四、獲取事件名稱
在整個拖拽過程中,我們無非就用到三種事件:開始、移動、結束。而在PC端和移動端分別對應一組事件名稱,我們將其分別用數組進行存儲。
eventName(){ if(isMobile()){ return ['touchstart','touchmove','touchend']; } else { return ['mousedown','mousemove','mouseup']; }}五、實現拖拽
前面準備工作做了這么多,就是為了實現這最最關鍵的一步:拖拽,也就是這三種事件(開始、移動、結束)的實現。
initData(){ // 父元素的位置 this.parentPos = { x: this.el.parentNode.getBoundingClientRect().left, y: this.el.parentNode.getBoundingClientRect().top, w: this.el.parentNode.getBoundingClientRect().width, h: this.el.parentNode.getBoundingClientRect().height, }; // 移動元素的初始位置和大小 this.elemPos = { x: this.el.offsetLeft, y: this.el.offsetTop, w: this.el.offsetWidth, h: this.el.offsetHeight, };}bindEvent(){ let eventName = this.eventName(), status = false; // 初始化數據 this.initData(); // 開始 this.el.addEventListener(eventName[0], e => { status = true; }); // 移動 document.addEventListener(eventName[1], e => { if(status){ e = this.eventObj(e); let left = e.clientX - this.elemPos.w / 2 - this.parentPos.x, top = e.clientY - this.elemPos.h / 2 - this.parentPos.y; this.el.style.cssText = `position: absolute; left: ${ left }px; top: ${ top }px;`; } }); // 結束 document.addEventListener(eventName[2], e => { e = this.eventObj(e,true); status = false; });}結束語
寫到這里,一個簡單的PC端和移動端雙端通用性JS拖拽插件就已經完成了。當然,我在此基礎上還加了拖拽目標和松開反彈等功能,完整代碼可在我的?Github[1]?上預覽。
References
[1]?Github:?https://github.com/chchlsh/drag
總結
以上是生活随笔為你收集整理的sortable 拖拽时互换目标的位置_双端通用型JS拖拽插件的封装与应用的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 消息称三星 Galaxy Watch 6
- 下一篇: 谷歌内部信证实公司将严格控管开销 大量福