【甜甜酱OH实践】OpenHarmony ArkUI实现Web API Drag拖拽效果

系统教程10个月前发布 wahs52034
5 0 0

​想了解更多内容,请访问:​

​51CTO和华为官方合作共建的鸿蒙技术社区​

​https://harmonyos.51cto.com​

大家经常浏览网页,是不是发现鼠标按住图片拖动后,可以拖出一个虚影。

【甜甜酱OH实践】OpenHarmony ArkUI实现Web API Drag拖拽效果

对于WEB前端,Web API提供drag接口,很简单就可以实现一个这样的效果。

对于OpenHarmony应用开发的小伙伴们,我们也可以利用onTouch实现一个这样的效果。

拖拽效果展示

【甜甜酱OH实践】OpenHarmony ArkUI实现Web API Drag拖拽效果

完整代码查看:

https://gitee.com/hytyj_hamstermie/tian-open-document.git

ArkUI实现Web API Drag拖拽效果/demo/page.ets

实现思路

实现思路很简单。

我们设置一个模拟块,这个模拟块是永远叠在最上面的。

模拟块一开始是隐藏的。

当用户触摸到绑定touch事件的一个固定块后,这个模拟块将拷贝该固定块的所有样式、内容,并获取该固定块的位置。

显示模拟块,并覆盖在固定块上。

在触摸事件持续过程中,模拟块跟随手指触摸坐标进行自身的位移更新。

在触摸事件结束后,模拟块消失。

【甜甜酱OH实践】OpenHarmony ArkUI实现Web API Drag拖拽效果

开发环境

  • IDE: DevEco Studio 3.0beta2 Build Version 3.0.0.800
  • SDK Version: 8
  • supportSystem “standard”
  • 系统版本:OpenHarmony 3.1beta

实践

效果如图:

【甜甜酱OH实践】OpenHarmony ArkUI实现Web API Drag拖拽效果

定义块样式

首先,我们先定义出固定块的样式。使用@Extend装饰器,我们可以更快地复制块样式。

使用@Extend装饰器

IDE中使用@Extend装饰器会报错Duplicate function implementation.

但OpenHarmony是支持@Extend装饰器的。推测可能属于IDE问题,这里推荐使用typescript忽略。

// 单行忽略
// @ts-ignore

// 忽略全文
// @ts-nocheck

// 取消忽略全文
// @ts-check

请慎用@ts-nocheck,之前因为方便,直接使用了忽略全文,导致调试时,很多语法问题没有修改回来。

我们给所有@Extend上面标注上// @ts-ignore

块样式

// @ts-ignore
@Extend(Flex) function blockStyle (opacity: number, backgroundColor: Color, width: number, height: number) {
.width(width)
.height(height)
.opacity(opacity)
.backgroundColor(backgroundColor)
}

块内文字样式

// @ts-ignore
@Extend(Text) function blockTextStyle (color: Color, size: number) {
.fontColor(color)
.fontSize(size)
.align(Alignment.Center)
}

注意:

@Extend装饰器方法是无法定义默认参数的

@Extend装饰器不能在struct中使用

定义固定块数据

@State装饰的变量是组件内部的状态数据,当这些状态数据被修改时,将会调用所在组件的build方法进行UI刷新。

//定义块组数据
@State blockInfos: any[] = [
{
text: "A",
textsize: 30,
color: "#ffffff",
bgcolor: "#336699",
opacity: 1
},
{
text: "B",
textsize: 30,
color: "#ffffff",
bgcolor: "#339961",
opacity: 1
},
{
text: "C",
textsize: 30,
color: "#ffffff",
bgcolor: "#929933",
opacity: 1
},
{
text: "D",
textsize: 30,
color: "#ffffff",
bgcolor: "#995033",
opacity: 1
}
]

简单块样式相关数据:

  • 文字内容
  • 文字大小
  • 文字颜色
  • 背景颜色
  • 透明度

定义渲染固定块方法

@Builder装饰器必须在struct内使用

@Builder装饰的方法可以定义默认参数

把主要传入参数放在最前:

  • 文字内容
  • 背景颜色
  • 透明度
  • touch事件方法
//可以设置默认参数
@Builder ItemBlock(
text:string,
bgcolor: Color,
opacity: number,
touchevent: any,
textcolor: Color = Color.White,
textsize: number = 30,
width: number = 90,
height: number = 90,
) {
Flex({
alignItems: ItemAlign.Center,
justifyContent: FlexAlign.Center
}) {
Text(text).blockTextStyle(textcolor, textsize)
}
.blockStyle(opacity, bgcolor, width, height)
.onTouch(touchevent)
}

定义渲染模拟块方法

定义模拟块相关状态数据

//位移坐标x
@State x: number = 0
//位移坐标y
@State y: number = 0
//是否显示
@State flayerShow: boolean = false
//模拟块宽度
@State flayerWidth: number = 0
//模拟块高度
@State flayerHeight: number = 0
//模拟块文字内容
@State flayerText: string = ""
//模拟块文字大小
@State flayerTextSize: number = 30
//模拟块文字颜色
@State flayerTextColor: Color = Color.White
//模拟块背景颜色
@State flayerBgColor: Color = Color.Black

实现

@Builder DragBlock(
text:string = this.flayerText,
bgcolor: Color = this.flayerBgColor,
textcolor: Color = this.flayerTextColor,
textsize: number = this.flayerTextSize,
width: number = this.flayerWidth,
height: number = this.flayerHeight,
opacity: number = 1
) {
Flex({
alignItems: ItemAlign.Center,
justifyContent: FlexAlign.Center
}) {
Text(this.flayerText).blockTextStyle(textcolor, textsize)
}
.blockStyle(opacity, bgcolor, width, height)
.position({
x: this.x,
y: this.y
})
}

布局

//堆叠容器
Stack() {
//固定块
Flex({
direction: FlexDirection.Column,
alignItems: ItemAlign.Center,
justifyContent: FlexAlign.Center
}) {
Grid() {
// OpenHarmony 3.1beta已修复ForEach返回index失效问题
ForEach(this.blockInfos, (info, index) => {
GridItem() {
Flex({
direction: FlexDirection.Column,
alignItems: ItemAlign.Center,
justifyContent: FlexAlign.SpaceAround
}) {
this.ItemBlock(
info.text,
info.bgcolor,
info.opacity,
(e: TouchEvent) => {
//注意这里,就是实现拖拽的重点
})
}
}
})
}
.columnsTemplate('1fr 1fr 1fr')
.rowsTemplate('1fr 1fr')
.backgroundColor("#dddddd")
.margin(20)
.height(300)
}
.backgroundColor("#f8f8f8")
.width("100%")
.height("100%")

//模拟块,叠在最上层,通过flayerShow控制其展示
if (this.flayerShow) {
this.DragBlock()
}
}
.width('100%')
.height('100%')

了解onTouch

参考HarmonyOS触摸事件:https://developer.harmonyos.com/cn/docs/documentation/doc-references/ts-universal-events-touch-0000001158261221

OpenHarmony区别

OpenHarmony对比HarmonyOS的API文档,在TouchEvent中多返回了一个target对象。

target携带了当前绑定触摸组件的基本信息,使我们实现一些能力更加便利。

TouchEvent信息示例:

{
"timestamp":345459939477,
"target":{
"area":{
"pos":{"x":0,"y":30},
"globalPos":{"x":88.33333333333333,"y":520},
"width":90,
"height":90
}
},
"touches":[{"type":0,"id":1,"screenX":126,"screenY":571.5,"x":37.66666793823242,"y":51.5}],
"type":0,
"changedTouches":[{"type":0,"id":1,"screenX":126,"screenY":571.5,"x":37.66666793823242,"y":51.5}]
}

触摸事件

【甜甜酱OH实践】OpenHarmony ArkUI实现Web API Drag拖拽效果

TouchEvent对象说明

属性

【甜甜酱OH实践】OpenHarmony ArkUI实现Web API Drag拖拽效果

TouchObject对象说明

【甜甜酱OH实践】OpenHarmony ArkUI实现Web API Drag拖拽效果

TouchType枚举说明

【甜甜酱OH实践】OpenHarmony ArkUI实现Web API Drag拖拽效果

TargetObject对象说明

【甜甜酱OH实践】OpenHarmony ArkUI实现Web API Drag拖拽效果

AreaObject对象说明

【甜甜酱OH实践】OpenHarmony ArkUI实现Web API Drag拖拽效果

实现onTouch方法

现在就让我们来实现最重要的onTouch方法啦

一个拖拽的实现会有三个阶段:

  • 手指按下,对应TouchType.Down
  • 手指拖动,对应TouchType.Move
  • 手指松开,对应TouchType.Up
if (e.type === TouchType.Down) {
//手指按下
} else if (e.type === TouchType.Move) {
//手指拖动
} else if (e.type === TouchType.Up) {
//手指松开
}

手指按下

在IDE中,获取到的坐标,组件的高宽,返回的数据类型并不是API文档中写的number而是一个Length类型。

Length是一个长度类型。

【甜甜酱OH实践】OpenHarmony ArkUI实现Web API Drag拖拽效果

由于之前我们定义的数据类型为number,所以这里我们可以使用as类型断言,将当前数据类型断言为number。

if (e.type === TouchType.Down) {
//拷贝块样式
let areainfo = e.target.area

//断言数据类型
this.flayerWidth = areainfo.width as number
this.flayerHeight = areainfo.height as number

this.flayerText = info.text
this.flayerTextSize = info.textsize
this.flayerTextColor = info.color
this.flayerBgColor = info.bgcolor

//获取初始坐标
//断言数据类型
this.x = areainfo.globalPos.x as number
this.y = areainfo.globalPos.y as number

//拷贝完成,展示模拟块
this.flayerShow = true

//将的对应块中的透明度数据设置为0.2
this.blockInfos[index].opacity = 0.2
}

手指拖动

将模拟块的坐标不断更新为手指的触摸坐标。

我们希望拖动的是块的中心点,那么就要将坐标做一个偏移处理。偏移量就是:块的宽度/2,块的高度/2。

else if (e.type === TouchType.Move) {
this.x = e.touches[0].screenX - this.flayerWidth / 2
this.y = e.touches[0].screenY - this.flayerHeight / 2
}

手指松开

手指松开后这个拖拽操作也终止了,那么就应该让这个模拟块消失了。

else if (e.type === TouchType.Up) {
this.flayerShow = false
this.blockInfos[index].opacity = 1
}

这样一个简单的拖拽就完成了,打开Previewer看看效果吧。

问题

虽然在模拟器上能很好运行,但是在开发板上出现了问题:

应用运行在开发板上时,TouchEvent内的所有值,都比应得值小了1倍,在进行拖动时,会发现位置有很大偏移。

这个问题已提交issue,希望OpenHarmony能够解决吧。

issue: https://gitee.com/openharmony/docs/issues/I4TF75

另外,由于缺少GPU支持,在开发板上会很卡。

开发板上查看

想要在开发板(3516/rk3568)上查看的小伙伴,可以将touchEvent返回数值*2就可以看到正常的效果。

if (e.type === TouchType.Down) {
let areainfo = e.target.area

//返回的高宽信息*2
this.flayerWidth = areainfo.width as number * 2
this.flayerHeight = areainfo.height as number * 2

this.flayerText = info.text
this.flayerTextSize = info.textsize
this.flayerTextColor = info.color
this.flayerBgColor = info.bgcolor
this.flayerShow = true

//返回的坐标*2
this.x = areainfo.globalPos.x as number * 2
this.y = areainfo.globalPos.y as number * 2

this.blockInfos[index].opacity = 0.2
}else if (e.type === TouchType.Move) {
this.x = e.touches[0].screenX * 2 - this.flayerWidth / 2
this.y = e.touches[0].screenY * 2 - this.flayerHeight / 2
}

​想了解更多内容,请访问:​

​51CTO和华为官方合作共建的鸿蒙技术社区​

​https://harmonyos.51cto.com​

【甜甜酱OH实践】OpenHarmony ArkUI实现Web API Drag拖拽效果

© 版权声明

相关文章