移動端實現拖拽的兩種方法

移動端的項目常常會引入手勢庫來實現拖拽css

不過若是隻是一兩個頁面用到拖拽,再引入一個手勢庫就很不划算vue

最近的項目中就有這麼一個需求:web

由於就這一個地方須要拖拽,因此我就沒有引入第三方庫app

 

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

1. 將元素設置爲固定定位,而後在拖拽的時候修改其定位,實現拖拽的效果;佈局

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

 

方案一:固定定位 fixed動畫

這種方案的核心就是給元素添加固定定位 position:fixed; this

但定位以後,元素會脫離文檔流,會影響原有但佈局spa

所以在開始拖拽 (觸發 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 的時候,元素有明顯的閃動

個人項目使用了 vue.js,但拖拽功能用到 vue 的地方很少,將幾個用於記錄的對象提出來,就能複用於其餘框架

相關文章
相關標籤/搜索