1. 程式人生 > 程式設計 >移動端JS實現拖拽兩種方法解析

移動端JS實現拖拽兩種方法解析

移動端的專案經常會引入手勢庫來實現拖拽

不過如果只是一兩個頁面用到拖拽,再引入一個手勢庫就很不划算

最近的專案中就有這麼一個需求:

移動端JS實現拖拽兩種方法解析

因為就這一個地方需要拖拽,所以我就沒有引入第三方庫

移動端的拖拽有兩種主流的實現方案:

1. 將元素設定為固定定位,然後在拖拽的時候修改其定位,實現拖拽的效果;

2. 使用 transform 中的平移translate 屬性實現拖拽。

方案一:固定定位 fixed

這種方案的核心就是給元素新增固定定位position:fixed;

但定位之後,元素會脫離文件流,會影響原有但佈局

因此在開始拖拽 (觸發touchstart事件) 的時候,需要將原本的元素 A 拷貝一份 (cloneNode())

給新元素 A2 新增定位,同時給原本的元素 A 設定visibility:hidden; 使之隱藏並佔位

1.1 建立遮罩

首先封裝一個建立遮罩的方法,用於放置拷貝出來的元素,並防止誤觸

createModal (id) {
 let modal = document.getElementById(id)
 if (!modal) { // 在沒有遮罩的時候建立遮罩
  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 開始拖拽

在觸發touchstart事件的時候,首先建立遮罩

並通過getBoundingClientRect()方法獲取到元素 A 的座標,記錄起點資訊

為了記錄起點資訊,需要 data 中建立一個物件source,用於記錄點選的位置 client,和初始定位座標 start

handleTouchstart (e) { // 開始拖拽
 // 建立遮罩層
 this.createModal(this.modalID) // modalID 遮罩層的id,由外部定義
 let element = e.targetTouches[0]
 let target = e.target.cloneNode(true) // 拷貝目標元素
 target.id = this.copyID // copyID 拷貝元素的id,由外部定義
 // 記錄初始點選位置 client,用於計算移動距離
 this.source.client = {
  x: element.clientX,y: element.clientY
 }
 // 算出目標元素的固定位置
 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 處理拖拽

拖拽的時候,監聽touchmove事件

用【當前滑鼠點位置】減去【初始點選位置】得到移動的距離

再結合初始座標資訊,更新拖拽元素的座標

handleTouchmove (e) { // 拖拽中
 let element = e.targetTouches[0]
 let target = document.getElementById(this.copyID)
 // 根據初始點選位置 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
 // 移動當前元素
 target.style.left = `${left}px`
 target.style.top = `${top}px`
},

1.4 拖拽結束

拖拽結束的時候,記錄終點位置,刪除遮罩

handleTouchend (e) { // 拖拽結束
 let end = {
  x: e.changedTouches[0].clientX,y: e.changedTouches[0].clientY
 }
 // 刪除遮罩層
 let modal = document.getElementById(this.modalID)
 document.body.removeChild(modal)
 // 處理結果
 this.doingSth(end)
},

不過上面的程式碼只實現了拖拽的功能,並沒有對目標元素 A 進行顯示/隱藏的操作

可以根據業務場景自行新增,或者參考方案二

方案二:平移動畫translate

這種方案更為簡單,不需要建立額外的 DOM 元素

只需對原本的元素新增 transform 屬性,甚至不需要 transition 屬性

然後在拖拽過程中,實時更新transform: translate(X,Y)中 x,y 的座標資訊,實現拖拽

2.1 開始拖拽

開始拖拽的時候,只需要記錄起點座標

handleTouchstart (e) { // 開始拖拽
 let element = e.targetTouches[0]// 記錄初始 client 位置,用於計算移動距離
 this.source.client = {
  x: element.clientX,y: element.clientY
 }
},

為了防止拖拽的過程中誤觸,建議使用方案一的createModal()方法建立一個遮罩

2.2 處理拖拽

根據當前座標和起點座標,計算出距離,然後更新 translate 的座標

handleTouchmove (e) { // 拖拽中
 let element = e.targetTouches[0]
 // 根據初始 client 位置計算移動距離
 let x = element.clientX - this.source.client.x
 let y = element.clientY - this.source.client.y
 // 移動當前元素
 element.target.style.cssText = `transform: translate(${x}px,${y}px);`
},

2.3 拖拽結束

拖拽完成後,清除平移動畫

handleTouchend (e) { // 拖拽結束
 // 清除拖拽樣式
 e.target.style.cssText = `transform: none;`
},

小結:

方案一在獲取目標元素的座標資訊的時候使用了 getBoundingClientRect() 方法

但這個方法效能不高,應當少用

而且即時使用了該方法,最後得到的 left 和 top 也不夠精確,touchstart 的時候,元素有明顯的閃動

以上就是本文的全部內容,希望對大家的學習有所幫助,也希望大家多多支援我們。