移動端JS實現(xiàn)拖拽兩種方法解析
移動端的項目經(jīng)常會引入手勢庫來實現(xiàn)拖拽
不過如果只是一兩個頁面用到拖拽,再引入一個手勢庫就很不劃算
最近的項目中就有這么一個需求:
因為就這一個地方需要拖拽,所以我就沒有引入第三方庫
移動端的拖拽有兩種主流的實現(xiàn)方案:
1. 將元素設(shè)置為固定定位,然后在拖拽的時候修改其定位,實現(xiàn)拖拽的效果;
2. 使用 transform 中的平移translate 屬性實現(xiàn)拖拽。
方案一:固定定位 fixed
這種方案的核心就是給元素添加固定定位position:fixed;
但定位之后,元素會脫離文檔流,會影響原有但布局
因此在開始拖拽 (觸發(fā)touchstart事件) 的時候,需要將原本的元素 A 拷貝一份 (cloneNode())
給新元素 A2 添加定位,同時給原本的元素 A 設(shè)置visibility:hidden; 使之隱藏并占位
1.1 創(chuàng)建遮罩
首先封裝一個創(chuàng)建遮罩的方法,用于放置拷貝出來的元素,并防止誤觸
createModal (id) { let modal = document.getElementById(id) if (!modal) { // 在沒有遮罩的時候創(chuàng)建遮罩 modal = document.createElement(’div’) modal.id = id modal.style.cssText = `position: fixed; left: 0; top: 0; right: 0; bottom: 0; z-index: 999;` document.body.appendChild(modal) }},
1.2 開始拖拽
在觸發(fā)touchstart事件的時候,首先創(chuàng)建遮罩
并通過getBoundingClientRect()方法獲取到元素 A 的坐標(biāo),記錄起點信息
為了記錄起點信息,需要 data 中創(chuàng)建一個對象source,用于記錄點擊的位置 client,和初始定位坐標(biāo) start
handleTouchstart (e) { // 開始拖拽 // 創(chuàng)建遮罩層 this.createModal(this.modalID) // modalID 遮罩層的id,由外部定義 let element = e.targetTouches[0] let target = e.target.cloneNode(true) // 拷貝目標(biāo)元素 target.id = this.copyID // copyID 拷貝元素的id,由外部定義 // 記錄初始點擊位置 client,用于計算移動距離 this.source.client = { x: element.clientX, y: element.clientY } // 算出目標(biāo)元素的固定位置 let disX = this.source.start.left = element.target.getBoundingClientRect().left let disY = this.source.start.top = element.target.getBoundingClientRect().top target.style.cssText = `position: fixed; left: ${disX}px; top: ${disY}px;` // 將拷貝的元素放到遮罩中 document.getElementById(this.modalID).appendChild(target)},
1.3 處理拖拽
拖拽的時候,監(jiān)聽touchmove事件
用【當(dāng)前鼠標(biāo)點位置】減去【初始點擊位置】得到移動的距離
再結(jié)合初始坐標(biāo)信息,更新拖拽元素的坐標(biāo)
handleTouchmove (e) { // 拖拽中 let element = e.targetTouches[0] let target = document.getElementById(this.copyID) // 根據(jù)初始點擊位置 client 計算移動距離 let left = this.source.start.left + element.clientX - this.source.client.x let top = this.source.start.top + element.clientY - this.source.client.y // 移動當(dāng)前元素 target.style.left = `${left}px` target.style.top = `${top}px`},
1.4 拖拽結(jié)束
拖拽結(jié)束的時候,記錄終點位置,刪除遮罩
handleTouchend (e) { // 拖拽結(jié)束 let end = { x: e.changedTouches[0].clientX, y: e.changedTouches[0].clientY } // 刪除遮罩層 let modal = document.getElementById(this.modalID) document.body.removeChild(modal) // 處理結(jié)果 this.doingSth(end)},
不過上面的代碼只實現(xiàn)了拖拽的功能,并沒有對目標(biāo)元素 A 進(jìn)行顯示/隱藏的操作
可以根據(jù)業(yè)務(wù)場景自行添加,或者參考方案二
方案二:平移動畫translate
這種方案更為簡單,不需要創(chuàng)建額外的 DOM 元素
只需對原本的元素添加 transform 屬性,甚至不需要 transition 屬性
然后在拖拽過程中,實時更新transform: translate(X, Y)中 x, y 的坐標(biāo)信息,實現(xiàn)拖拽
2.1 開始拖拽
開始拖拽的時候,只需要記錄起點坐標(biāo)
handleTouchstart (e) { // 開始拖拽 let element = e.targetTouches[0]// 記錄初始 client 位置,用于計算移動距離 this.source.client = { x: element.clientX, y: element.clientY }},
為了防止拖拽的過程中誤觸,建議使用方案一的createModal()方法創(chuàng)建一個遮罩
2.2 處理拖拽
根據(jù)當(dāng)前坐標(biāo)和起點坐標(biāo),計算出距離,然后更新 translate 的坐標(biāo)
handleTouchmove (e) { // 拖拽中 let element = e.targetTouches[0] // 根據(jù)初始 client 位置計算移動距離 let x = element.clientX - this.source.client.x let y = element.clientY - this.source.client.y // 移動當(dāng)前元素 element.target.style.cssText = `transform: translate(${x}px, ${y}px);`},
2.3 拖拽結(jié)束
拖拽完成后,清除平移動畫
handleTouchend (e) { // 拖拽結(jié)束 // 清除拖拽樣式 e.target.style.cssText = `transform: none;`},
小結(jié):
方案一在獲取目標(biāo)元素的坐標(biāo)信息的時候使用了 getBoundingClientRect() 方法
但這個方法性能不高,應(yīng)當(dāng)少用
而且即時使用了該方法,最后得到的 left 和 top 也不夠精確,touchstart 的時候,元素有明顯的閃動
以上就是本文的全部內(nèi)容,希望對大家的學(xué)習(xí)有所幫助,也希望大家多多支持好吧啦網(wǎng)。
相關(guān)文章:
1. ASP 信息提示函數(shù)并作返回或者轉(zhuǎn)向2. asp(vbs)Rs.Open和Conn.Execute的詳解和區(qū)別及&H0001的說明3. CSS hack用法案例詳解4. PHP設(shè)計模式中工廠模式深入詳解5. 用css截取字符的幾種方法詳解(css排版隱藏溢出文本)6. asp中response.write("中文")或者js中文亂碼問題7. ASP.NET MVC遍歷驗證ModelState的錯誤信息8. ThinkPHP5實現(xiàn)JWT Token認(rèn)證的過程(親測可用)9. ASP 處理JSON數(shù)據(jù)的實現(xiàn)代碼10. .NET中l(wèi)ambda表達(dá)式合并問題及解決方法
