本文正在參加星光計劃3.0–夏日挑戰賽
【FFH】實作元件拖拽(OpenHarmony JS UI)
原本打算實作canvas畫布的拖拽效果,過程中嘗試過幾種方式,總結了一下分享出來。
Demo效果

實作拖拽的效果主要有兩種方式:
- 使用鴻蒙JS API官方文檔提供的Drag事件 *
-
使用傳統html方式的Touch事件
這兩種方式就目前實作拖拽效果來看并沒有太大的差别,Move事件已經能滿足大多數需要擷取坐标的場景比如拖拽效果,至于新提供的Drag事件API還有什麼特别的地方以後再繼續探究。
*Ark UI
據我了解,傳統的Web前端開發中,canvas是不支援move移動事件的,在但是鴻蒙自研的Ark UI架構中很好地改進了這一點,既移植了Web前端開發的優點(js+hml+css這一架構就對前端頁面的開發很友好),又改進了不少它的缺點,使得Move事件的編寫變得更簡潔更易上手,而Web開發中需要在canvas外層套上一層個div容器,通過div來間接拖拽。
拖拽原理
元件位置實時跟随手指觸屏坐标變化,鴻蒙JS UI提供了hml和js資料雙向綁定的特性,是以可以很友善的進行資料更新。
拖拽實作
Drag
Drag事件包含三個事件對象DragEven,dragstart(開始拖拽)、drag(拖拽過程中)、dragend(拖拽結束)
<div style="position : absolute; border-color : red; border-width : 2px;
left : {{ left }}; top : {{ top }}; width : {{w}}px; height : {{h}}px" ondrag="ondrag"
class="canvas">
<canvas ref="canvas_1" style="width : 568px; height : 720px"></canvas>
</div>
hml中使用div包裹canvas畫布,并将樣式中的left和top與js進行綁定,并在data{}中初始化。
export default {
data: {
top: 600, //---距離螢幕頂部的距離
left: 80, //---距離螢幕左端的距離
w: 570, //---畫布寬高
h:690
},
}
編寫ondrag對應的方法:
ondrag(e){
let nx = e.globalX;
let ny = e.globalY;
this.left = nx;
this.top = ny;
}
上面利用了drag事件提供的除了通用屬性外的額外屬性:globalX和globalY——擷取目前(全屏)觸點的坐标(相對于左上角)。
下面引用官方文檔的解釋:
DragEvent對象屬性清單(繼承BaseEvent)7+
屬性 | 類型 | 說明 |
---|---|---|
type | string | 事件名稱 |
globalX | number | 距離螢幕左上角坐标原點橫向距離 |
globalY | number | 距離螢幕左上角坐标原點縱向距離 |
timestamp | number | 時間戳 |
是以,可以通過将globalX、globalY指派給div的left和top使元件位置改變。
問題
僅僅是這樣的話,觸點的位置一直指向div元件的左上角,因為div的左上角坐标始終跟觸點的坐标綁定。
那如何讓圖檔始終跟随手指按下的位置開始移動?
改進
- 擷取觸摸開始時的坐标
- 計算移動坐标使得剛開始觸摸的位置跟随手指移動
dragStart(e){
x = e.globalX;
y = e.globalY;
},
ondrag(e){
let nx = e.globalX;
let ny = e.globalY;
// console.info("left="+ (nx-x))
// console.info("top="+ (ny-y))
if(nx+570-x<=gloX && ny+690-y<=gloY && nx-x>0 && ny-y>0){ //---這裡screenX、screenY為螢幕寬高,避免超出螢幕範圍
this.left = nx-x;
this.top = ny-y;
}
}
這樣便簡單實作了元件根據手指開始觸屏位置跟随移動的功能。
Touch
利用Touch事件的方式跟Drag的原理一樣,最大差別隻在于系統擷取坐标位置的所提供的方法不同.
moveStart(e){
x = e.touches[0].globalX;
y = e.touches[0].globalY;
},
onMove(e){
let nx = e.touches[0].globalX;
let ny = e.touches[0].globalY;
if(nx+this.screenX-x<=gloX && ny+this.screenY-y<=gloY && nx-x>0 && ny-y>0){
this.left = nx-x;
this.top = ny-y;
}
}
Tips
在用Drag事件寫的時候踩過坑:使用Previewer調試的時候,我發現用Drag事件寫的拖拽效果體驗不太好,而move事件能很流暢地進行拖拽,而Drag有時候能觸發dragStart事件但是元件并不移動。(也許是跟調試器有關)
最終調試時,把
dragStart(e){
x = e.globalX;
y = e.globalY;
},
換成
moveStart(e){
x = e.touches[0].globalX;
y = e.touches[0].globalY;
},
瞬間流暢了,而移動“時”事件使用onMove或onDrag的效果都沒有差别。