移動(dòng)端JS實(shí)現(xiàn)拖拽兩種方法解析
移動(dòng)端的項(xiàng)目經(jīng)常會(huì)引入手勢(shì)庫(kù)來(lái)實(shí)現(xiàn)拖拽
不過(guò)如果只是一兩個(gè)頁(yè)面用到拖拽,再引入一個(gè)手勢(shì)庫(kù)就很不劃算
最近的項(xiàng)目中就有這么一個(gè)需求:

因?yàn)榫瓦@一個(gè)地方需要拖拽,所以我就沒(méi)有引入第三方庫(kù)
移動(dòng)端的拖拽有兩種主流的實(shí)現(xiàn)方案:
1. 將元素設(shè)置為固定定位,然后在拖拽的時(shí)候修改其定位,實(shí)現(xiàn)拖拽的效果;
2. 使用 transform 中的平移translate 屬性實(shí)現(xiàn)拖拽。
方案一:固定定位 fixed
這種方案的核心就是給元素添加固定定位position:fixed;
但定位之后,元素會(huì)脫離文檔流,會(huì)影響原有但布局
因此在開(kāi)始拖拽 (觸發(fā)touchstart事件) 的時(shí)候,需要將原本的元素 A 拷貝一份 (cloneNode())
給新元素 A2 添加定位,同時(shí)給原本的元素 A 設(shè)置visibility:hidden; 使之隱藏并占位
1.1 創(chuàng)建遮罩
首先封裝一個(gè)創(chuàng)建遮罩的方法,用于放置拷貝出來(lái)的元素,并防止誤觸
createModal (id) {
let modal = document.getElementById(id)
if (!modal) { // 在沒(méi)有遮罩的時(shí)候創(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 開(kāi)始拖拽
在觸發(fā)touchstart事件的時(shí)候,首先創(chuàng)建遮罩
并通過(guò)getBoundingClientRect()方法獲取到元素 A 的坐標(biāo),記錄起點(diǎn)信息
為了記錄起點(diǎn)信息,需要 data 中創(chuàng)建一個(gè)對(duì)象source,用于記錄點(diǎn)擊的位置 client,和初始定位坐標(biāo) start
handleTouchstart (e) { // 開(kāi)始拖拽
// 創(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,由外部定義
// 記錄初始點(diǎn)擊位置 client,用于計(jì)算移動(dòng)距離
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 處理拖拽
拖拽的時(shí)候,監(jiān)聽(tīng)touchmove事件
用【當(dāng)前鼠標(biāo)點(diǎn)位置】減去【初始點(diǎn)擊位置】得到移動(dòng)的距離
再結(jié)合初始坐標(biāo)信息,更新拖拽元素的坐標(biāo)
handleTouchmove (e) { // 拖拽中
let element = e.targetTouches[0]
let target = document.getElementById(this.copyID)
// 根據(jù)初始點(diǎn)擊位置 client 計(jì)算移動(dòng)距離
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)當(dāng)前元素
target.style.left = `${left}px`
target.style.top = `${top}px`
},
1.4 拖拽結(jié)束
拖拽結(jié)束的時(shí)候,記錄終點(diǎn)位置,刪除遮罩
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)
},
不過(guò)上面的代碼只實(shí)現(xiàn)了拖拽的功能,并沒(méi)有對(duì)目標(biāo)元素 A 進(jìn)行顯示/隱藏的操作
可以根據(jù)業(yè)務(wù)場(chǎng)景自行添加,或者參考方案二
方案二:平移動(dòng)畫(huà)translate
這種方案更為簡(jiǎn)單,不需要?jiǎng)?chuàng)建額外的 DOM 元素
只需對(duì)原本的元素添加 transform 屬性,甚至不需要 transition 屬性
然后在拖拽過(guò)程中,實(shí)時(shí)更新transform: translate(X, Y)中 x, y 的坐標(biāo)信息,實(shí)現(xiàn)拖拽
2.1 開(kāi)始拖拽
開(kāi)始拖拽的時(shí)候,只需要記錄起點(diǎn)坐標(biāo)
handleTouchstart (e) { // 開(kāi)始拖拽
let element = e.targetTouches[0]// 記錄初始 client 位置,用于計(jì)算移動(dòng)距離
this.source.client = {
x: element.clientX,
y: element.clientY
}
},
為了防止拖拽的過(guò)程中誤觸,建議使用方案一的createModal()方法創(chuàng)建一個(gè)遮罩
2.2 處理拖拽
根據(jù)當(dāng)前坐標(biāo)和起點(diǎn)坐標(biāo),計(jì)算出距離,然后更新 translate 的坐標(biāo)
handleTouchmove (e) { // 拖拽中
let element = e.targetTouches[0]
// 根據(jù)初始 client 位置計(jì)算移動(dòng)距離
let x = element.clientX - this.source.client.x
let y = element.clientY - this.source.client.y
// 移動(dòng)當(dāng)前元素
element.target.style.cssText = `transform: translate(${x}px, ${y}px);`
},
2.3 拖拽結(jié)束
拖拽完成后,清除平移動(dòng)畫(huà)
handleTouchend (e) { // 拖拽結(jié)束
// 清除拖拽樣式
e.target.style.cssText = `transform: none;`
},
小結(jié):
方案一在獲取目標(biāo)元素的坐標(biāo)信息的時(shí)候使用了 getBoundingClientRect() 方法
但這個(gè)方法性能不高,應(yīng)當(dāng)少用
而且即時(shí)使用了該方法,最后得到的 left 和 top 也不夠精確,touchstart 的時(shí)候,元素有明顯的閃動(dòng)
以上就是本文的全部?jī)?nèi)容,希望對(duì)大家的學(xué)習(xí)有所幫助,也希望大家多多支持腳本之家。
- JS實(shí)現(xiàn)簡(jiǎn)易的圖片拖拽排序?qū)嵗a
- js實(shí)現(xiàn)限定范圍拖拽的示例
- js實(shí)現(xiàn)拖拽與碰撞檢測(cè)
- JS實(shí)現(xiàn)拖拽元素時(shí)與另一元素碰撞檢測(cè)
- JQuery Dialog(JS 模態(tài)窗口,可拖拽的DIV)
- Sortable.js拖拽排序使用方法解析
- js實(shí)現(xiàn)拖拽效果
- javascript實(shí)現(xiàn)移動(dòng)端上的觸屏拖拽功能
- JS實(shí)現(xiàn)漂亮的窗口拖拽效果(可改變大小、最大化、最小化、關(guān)閉)
- JS組件Bootstrap Table表格行拖拽效果實(shí)現(xiàn)代碼
- js實(shí)現(xiàn)簡(jiǎn)易拖拽的示例
相關(guān)文章
移動(dòng)端touch拖動(dòng)和click事件沖突問(wèn)題解決
這篇文章主要為大家介紹了移動(dòng)端touch拖動(dòng)和click事件沖突問(wèn)題解決,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪2022-09-09
非主流的textarea自增長(zhǎng)實(shí)現(xiàn)js代碼
今天稍微研究了下textarea隨輸入內(nèi)容自動(dòng)增長(zhǎng)的功能,通過(guò)google參考了一些實(shí)現(xiàn)方式2011-12-12
Bootstrap php制作動(dòng)態(tài)分頁(yè)標(biāo)簽
這篇文章主要為大家詳細(xì)介紹了Bootstrap php制作動(dòng)態(tài)分頁(yè)標(biāo)簽的相關(guān)資料,具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2016-12-12

