#夏日挑战赛#【FFH】实现组件拖拽(OpenHarmony JS UI) 原创 精华
本文正在参加星光计划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的效果都没有差别。
AMG手机能实现操作吗?
这个实现效果很棒