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

原创
系统 OpenHarmony
大家经常浏览网页,是不是发现鼠标按住图片拖动后,可以拖出一个虚影。对于WEB前端,Web API提供drag接口,很简单就可以实现一个这样的效果。

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

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

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

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

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

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

拖拽效果展示

完整代码查看:

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

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

实现思路

实现思路很简单。

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

模拟块一开始是隐藏的。

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

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

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

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

开发环境

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

实践

效果如图:

定义块样式

首先,我们先定义出固定块的样式。使用@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}]
}

触摸事件

TouchEvent对象说明

属性

TouchObject对象说明

TouchType枚举说明

TargetObject对象说明

AreaObject对象说明

实现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是一个长度类型。

由于之前我们定义的数据类型为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​


责任编辑:jianghua 来源: 鸿蒙社区
相关推荐

2022-09-20 14:35:59

ArkUI鸿蒙JS

2022-03-29 10:04:44

APIHarmony文件管理

2024-01-11 15:54:55

eTS语言TypeScript应用开发

2022-09-21 14:51:21

ArkUI信件弹出

2022-07-26 14:40:42

ArkUIJS

2022-09-15 15:04:16

ArkUI鸿蒙

2012-05-17 13:17:26

HTML5

2022-05-27 14:55:34

canvas画布鸿蒙

2023-08-17 15:01:08

ArkUI布局渲染

2022-07-20 15:32:25

时钟翻页Text组件

2022-08-04 13:55:08

拼数字小游戏鸿蒙

2023-04-04 09:20:34

ArkUI下载进度条

2023-08-17 15:04:22

2011-10-27 16:24:48

API

2022-09-02 15:17:04

ArkUI鸿蒙

2023-03-13 15:03:05

鸿蒙ArkUI

2022-08-12 19:13:07

etswifi连接操作

2020-12-09 07:54:17

Vue插件鼠标

2022-08-23 16:07:02

ArkUI鸿蒙

2022-06-27 14:12:32

css鸿蒙自定义
点赞
收藏

51CTO技术栈公众号