2

【甜甜酱OH实践】OpenHarmony ArkUI实现Web API Drag拖拽效果-51CTO.COM

 2 years ago
source link: https://os.51cto.com/article/701824.html
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.
【甜甜酱OH实践】OpenHarmony ArkUI实现Web API Drag拖拽效果-51CTO.COM
【甜甜酱OH实践】OpenHarmony ArkUI实现Web API Drag拖拽效果 原创
作者:拓维信息田怡婧 2022-02-17 17:05:31
大家经常浏览网页,是不是发现鼠标按住图片拖动后,可以拖出一个虚影。对于WEB前端,Web API提供drag接口,很简单就可以实现一个这样的效果。

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

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

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

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

46c4c5028f8ded79a78280c19e9afd3799c72a.png

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

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

拖拽效果展示

25bdb0739a442f34a91256936c71f742bb8f00.gif

完整代码查看:

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

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

实现思路很简单。

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

模拟块一开始是隐藏的。

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

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

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

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

16e4d8656b97535317e5363400351ec4777b73.png

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

效果如图:

f1e7b490802da2c0b3e05665c9ebd848514a3c.png

定义块样式

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

12cc7e827657650ade762170e5584c09132a7f.png

TouchEvent对象说明

794893b794270f5fb3d386e0512b51b007d6c3.png

TouchObject对象说明

4506a2488437b0e9401574cb037b9b557beed3.png

TouchType枚举说明

917151e3215596402ea265d47a1dc26acbad42.png

TargetObject对象说明

535dca0689c2aefd522929c3c7420cae7c19b1.png

AreaObject对象说明

42767bc42148592791b8535fc1d05c68a71d4c.png

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

d3ea5c7327262079ce48016004ad87ade238aa.png

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

47e0c0f90506c06644320197e0b4b47e5b39a8.jpg

责任编辑:jianghua 来源: 鸿蒙社区
zanpc.bd208a1.pngzanpchover.fdd60ba.png
weixin.23cd8b3.png 分享到微信
weibo.16d6b4f.png 分享到微博

About Joyk


Aggregate valuable and interesting links.
Joyk means Joy of geeK