#打卡不停更#harmonyos基于arkui(ets)实现雷达扫描(代码片段)

author author     2022-12-06     172

关键词:

作者:杨尚晓

前言

雷达扫描是一个比较有科技感的东西,对于科幻迷来说,科幻电影里基本都能看到的画面,一个大大的屏幕上,可以看到雷达扫描的绿幕效果。 下面我们使用三种方式来实现这样的雷达效果。

项目说明

  • 工具版本:DevEco Studio 3.0 Release
  • SDK版本:3.1.1.2(API Version 8 Release)
  • 主要组件:canvas, Row, Image

效果

  • 使用Image图片组件方式实现

  • 使用canvas组件实现

  • 使用Row组件的渐变属性实现

实现过程

需要注意,一个页面里只能有一个@Entry修饰符,所以,下面三种方法在预览的时候,需要注意注释只保留一个@Entry

1. 使用Image方法实现

使用Image组件的方法是最简单的,直接制作两张图片,一张底图,一张扫描的图

将两张图片通过叠加,将扫描的图片通过圆心宣旋转即可,下面使用代码来实现

@Entry
@Component
struct RadarImg 
  @State angle:number = 0;
  aboutToAppear()
    setTimeout(()=>
      this.angle = 360
    ,200)
  
  build()
    Row()
      Stack()
        Image($r("app.media.radar_grid"))
          .width(300)
          .height(300)
        Image($r(app.media.radar_sector))
          .width(300)
          .height(300)
          .rotate(
            z: 1,
            angle: this.angle
          )
          .animation(
            duration: 2000,
            curve:Curve.Linear,
            iterations: -1,
          )
      
    
    .justifyContent(FlexAlign.Center)
    .backgroundColor(0x111111)
    .width(100%)
    .height(100%)
  

整体比较简单,旋转主要用到了animation属性,这些在官网API文档可以查看。虽然使用Image组件实现比较简单,但是却是可以实现一些复杂的雷达UI。

2. 使用canvas实现

使用canvas实现的需要用到两个组件,第一个是Canvas组件,用来绘制底图网格,第二个是Row组件,使用角渐变属性实现旋转的扇形。 这里为什么不都使用canvas实现呢,找了一圈,canvas只有线性渐变和径向渐变,切没有角度渐变属性,所以,为了方便就用了row来实现吧。 下面直接上代码。

Row的渐变方式在下一个方法讲解,最终都还是使用animation属性动画实现扇形的旋转效果

@Entry
@Component
struct RadarCanvas 
  private settings: RenderingContextSettings = new RenderingContextSettings(true);
  private ctx: CanvasRenderingContext2D = new CanvasRenderingContext2D(this.settings)
  @State angle:number = 0
  // 绘制网格
  drawGrid = (): void => 
    let ctx = this.ctx;
    ctx.lineWidth = 1
    ctx.strokeStyle = green

    // arr数组是需要绘制的底图圈圈
    let arr:Array<number> = [150, 100, 50]
    for(let i = 0; i < arr.length; i++)
      ctx.beginPath()
      ctx.arc(150, 150, arr[i] - 0.5, 0, 2 * Math.PI)
      ctx.stroke()
    

    // 绘制十字架
    ctx.beginPath()
    ctx.moveTo(0,150)
    ctx.lineTo(300,150)
    ctx.stroke()

    ctx.beginPath()
    ctx.moveTo(150,0)
    ctx.lineTo(150,300)
    ctx.stroke()
  

  aboutToAppear()
    setTimeout(()=>
      this.angle = 360
    ,200)
  
  build()
    Row()
      Stack()
        Canvas(this.ctx)
          .onReady(()=>
            this.drawGrid()
          )
          .width(300)
          .height(300)
        Row()
          .width(300)
          .height(300)
          .borderRadius(150)
          .sweepGradient(
            center: [150,150],
            start: 0,
            end: 359,
            colors: [
              [rgba(0,0,0,0),0],
              [rgba(0,0,0,0),0.4],
              [rgba(0,255,0,0.5),1],
            ]
          )
          .rotate(
            z: 1,
            angle: this.angle
          )
          .animation(
            duration: 2000,
            iterations: -1,
            curve: Curve.Linear
          )
      
    
      .justifyContent(FlexAlign.Center)
      .backgroundColor(0x111111)
      .width(100%)
      .height(100%)
  


3. 使用Row组件实现

使用Row组件实现的方法稍微复杂一些,这里用到了4个Row组件,其实对于前端童鞋来说,这里可能会比较好里一些,其实就是类似通过div和css来实现的,row组件是div,其属性是css样式。

废话不多说,直接上代码开搞

3.1 实现圆圈圈

首先使用径向渐变属性(radialGradient)来实现底部圈圈的效果 radialGradient属性有几个需要注意的值,radius是渐变的半径,这里使用30。 渐变颜色colors组用了三个数据,可以看出,前两个其实是透明度为0的。 可以理解为

  • 第一个渐变颜色到第二个渐变颜色用了90%
  • 第二个渐变颜色到第三个渐变颜色,用了10%(100%-90%)
  • 也就是说在渐变半径为30的情况下,有90%是透明的,只有10%是透明到green颜色的,这样得到了一个圈圈
  • 然后又设置了repeating属性(重复着色)为true
  • 所以在半径为150的圆内,可以设置 150 / 3 = 5个圆圈圈
Row()
.width(300)
.height(300)
.borderRadius(150)
.radialGradient(
    center: [150,150],
    radius: 30,
    colors: [
      [rgba(0,0,0,0), 0],
      [rgba(0,0,0,0), 0.9],
      [green, 1],
    ],
    repeating: true,
)

我们看看效果

3.2 实现十字架

实现十字架使用了两个Row组件,使用线性渐变属性(linearGradient)分别绘制了一横一竖的效果。 可以看到颜色组colors使用了5个颜色来实现,1,2,4,5的颜色都是透明的

  • 第一个颜色到第二个颜色都是透明的,渐变范围是0到49%,
  • 第二个颜色到第三个颜色渐变范围是50% - 49% = 1%,也就是在其50%(中间)的地方绘制了一个1%的green线条
  • 第三个颜色到第四个颜色不变
  • 第三个到第五个颜色也是透明,渐变范围也是50%

上面绘制好之后,我们通过angle属性将线条旋转90度得到一个十字架

Row()
  .width(300)
  .height(300)
  .borderRadius(150)
  .linearGradient(
    angle: 0,
    colors: [
      [rgba(0,0,0,0), 0],
      [rgba(0,0,0,0),0.49],
      [green,0.5],
      [rgba(0,0,0,0),0.5],
      [rgba(0,0,0,0),1]
    ]
  )
Row()
  .width(300)
  .height(300)
  .borderRadius(150)
  .linearGradient(
    angle: 90,
    colors: [
      [rgba(0,0,0,0), 0],
      [rgba(0,0,0,0),0.49],
      [green,0.5],
      [rgba(0,0,0,0),0.5],
      [rgba(0,0,0,0),1]
    ]
  )

来看看效果

3.3 实现旋转扇形

扇形也是用到Row组件,其角度渐变属性(sweepGradient)来实现的。

Row()
  .width(300)
  .height(300)
  .borderRadius(150)
  .sweepGradient(
    center: [150,150],
    start: 0,
    end: 359,
    colors: [
      [rgba(0,0,0,0),0],
      [rgba(0,0,0,0),this.flag],
      [rgba(0,255,0,0.5),1],
    ]
  )

来看看效果

最后设置rotate旋转属性,设置旋转轴为z轴,角度angle为动态更新,这样animation动画属性才会更新。 最终代码 扇形的代码

Row()
  .width(300)
  .height(300)
  .borderRadius(150)
  .sweepGradient(
    center: [150,150],
    start: 0,
    end: 359,
    colors: [
      [rgba(0,0,0,0),0],
      [rgba(0,0,0,0),this.flag],
      [rgba(0,255,0,0.5),1],
    ]
  )
  .rotate(
    z: 1,
    angle: this.angle
  )
  .animation(
    duration: 2000,
    iterations: -1,
    curve: Curve.Linear
  )

这里添加一个扫描周边设备的效果,动态设置了一个数据源,通过ForEach来动态渲染

ForEach(this.scanData,(item: any) => 
  Column()
    Image($r(app.media.icon))
      .width(28)
      .height(28)
      .backgroundColor(#fff)
      .borderRadius(19)
    Text(item.name)
      .fontColor(#fff)
      .margin(top: 5)
      .fontSize(10)
  
  .alignItems(HorizontalAlign.Center)
  .position(x: item.x, y: item.y)
  .scale(x: this.w, y: this.h)
  .animation(
    duration: 1000,
    iterations: 1,
    curve: Curve.Friction
  )
  .opacity(this.opt)
)

最终的代码

// 雷达扫描组件
@Entry
@Component
struct Radar 
  @State angle:number = 0;
  @State scanData: any = []
  @State w:number = 0;
  @State h:number = 0;
  @State opt:number = 1
  @State flag:number = 0.4
  aboutToAppear()
    setTimeout(()=>
      this.angle = 360
    ,200)
    setTimeout(()=>
      animateTo(
        duration: 1000, // 动画时长
        curve: Curve.Linear, // 动画曲线
        iterations: -1, // 播放次数
        playMode: PlayMode.AlternateReverse, // 动画模式
        onFinish: () => 
          console.info(play end)
        
      , () => 
        this.opt = 0.3
      )
    ,2000)
    setTimeout(()=>
      this.scanData = [
        
          id: 1,
          x: 190,
          y: 200,
          name: 空调
        ,
        
          id: 1,
          x: 80,
          y: 240,
          name: 插座
        ,
      ]
      setTimeout(()=>
        this.w = 1;
        this.h = 1;
      ,200)
    ,1000)

  
  build()
    Row()
      Stack()
        Row()
          .width(300)
          .height(300)
          .borderRadius(150)
          .radialGradient(
            center: [150,150],
            radius: 30,
            colors: [
              [rgba(0,0,0,0), 0],
              [rgba(0,0,0,0), 0.9],
              [green, 1],
            ],
            repeating: true,
          )
        Row()
          .width(300)
          .height(300)
          .borderRadius(150)
          .linearGradient(
            angle: 0,
            colors: [
              [rgba(0,0,0,0), 0],
              [rgba(0,0,0,0),0.49],
              [green,0.5],
              [rgba(0,0,0,0),0.5],
              [rgba(0,0,0,0),1]
            ]
          )
        Row()
          .width(300)
          .height(300)
          .borderRadius(150)
          .linearGradient(
            angle: 90,
            colors: [
              [rgba(0,0,0,0), 0],
              [rgba(0,0,0,0),0.49],
              [green,0.5],
              [rgba(0,0,0,0),0.5],
              [rgba(0,0,0,0),1]
            ]
          )
        Row()
          .width(300)
          .height(300)
          .borderRadius(150)
          .sweepGradient(
            center: [150,150],
            start: 0,
            end: 359,
            colors: [
              [rgba(0,0,0,0),0],
              [rgba(0,0,0,0),this.flag],
              [rgba(0,255,0,0.5),1],
            ]
          )
          .rotate(
            z: 1,
            angle: this.angle
          )
          .animation(
            duration: 2000,
            iterations: -1,
            curve: Curve.Linear
          )
        ForEach(this.scanData,(item: any) => 
          Column()
            Image($r(app.media.icon))
              .width(28)
              .height(28)
              .backgroundColor(#fff)
              .borderRadius(19)
            Text(item.name)
              .fontColor(#fff)
              .margin(top: 5)
              .fontSize(10)
          
          .alignItems(HorizontalAlign.Center)
          .position(x: item.x, y: item.y)
          .scale(x: this.w, y: this.h)
          .animation(
            duration: 1000,
            iterations: 1,
            curve: Curve.Friction
          )
          .opacity(this.opt)
        )
      
      .width(300)
      .height(300)
    
    .justifyContent(FlexAlign.Center)
    .width(100%)
    .height(100%)
    .backgroundColor(0x111111)
  

来看看最终完整的效果

git地址

https://gitee.com/yango520/yg-radar

总结

通过这个雷达demo,特别是第三种方法,可以学到了颜色渐变属性中的三种渐变,线性渐变,径向渐变,和角度渐变。也可以学习属性动画的实现。

更多原创内容请关注:中软国际 HarmonyOS 技术团队

入门到精通、技巧到案例,系统化分享HarmonyOS开发技术,欢迎投稿和订阅,让我们一起携手前行共建鸿蒙生态。

想了解更多关于开源的内容,请访问:​

​51CTO 开源基础软件社区​

​https://ost.51cto.com/#bkwz​

#打卡不停更#harmonyos-基于arkui(ets)实现心电图组件(代码片段)

作者:杨尚晓前言随着大家生活水平的提升,越来越多的人注重自身身心健康,养生成了目前比较热门的活动。心电图是检测心脏活动状态的直观表现,可以通过心电图来观察人提的健康状况。响应鸿蒙万物互联的口号,肯定少... 查看详情

#打卡不停更#openharmony-arkui(ts)声明式开发之列表拖动排列(代码片段)

...基于TS扩展的声明式开发范式,因为OpenHarmony的API相对于HarmonyOS的API,功能上比较完善和成熟的,有些新的技术也早早接触到,所以本项目直接使用OpenHarmonySDK开发。工具版本:DevEcoStudio3.0ReleaseSDK版本: 查看详情

基于openharmony/harmonyos操作系统的arkui框架——harmony原生开发

一.基于OpenHarmony/HarmonyOS操作系统的ArkUI框架——Harmony原生开发开发需要的IDE:1.1什么是ArkUI框架?ArkUI是一套构建分布式应用界面的声明式UI开发框架。它使用极简的UI信息语法、丰富的UI组件、以及实时界面预览工具,帮助您提... 查看详情

harmonyos-基于arkui(js)实现图片旋转验证(代码片段)

...习其他人的slider滑块组件衍生出的小组件,本文主要结合HarmonyOS官网上的相关组件以及通用API,来实现一个图片的旋转验证--需拖动滑块将图片旋转还原为正确,方可验证通过。效果演示实现原理触发条件基于HarmonyOS通用事件touc... 查看详情

harmonyos-基于arkui(js)实现彩带飘动特效(代码片段)

...购物是经常看到一个彩带飘动的特效,又恰逢最近在学习HarmonyOS开发的知识,便想着自己能否用HarmonyOS相关的知识也做一个类似的东西,于是就自己动手尝试了一下。效果展示实现原理彩带飘动特效,主要是使用canvas来实现的,... 查看详情

harmonyos-基于arkui(ets)实现猫头鹰动画(代码片段)

作者:范颖前言因为工作原因,后面我可能会接触到基于TS扩展的声明是开发范式,因此我需要提前学习关于ets的内容。在学习了一段时间之后,我决定用ets来画一只猫头鹰,看看ets跟我之前掌握的知识有何不同,在什么地方需... 查看详情

#夏日挑战赛#harmonyos-基于arkui(js)实现打地鼠游戏(代码片段)

...:尹宝荣本文正在参加星光计划3.0–夏日挑战赛前言初学HarmonyOSArkUI(JS),对于FA的开发还是不太熟悉,单纯看文档,不使用起来的话,始终掌握不了,代码还是要多敲多思考才能进步。所以周末突发奇想利用HarmonyOS写了一个简单的... 查看详情

harmonyos-基于arkui(js)实现信件弹出效果(代码片段)

作者:罗晓纯前言自从大家使用QQ、微信、邮件等网络平台交流以后,大家对纸这种介质和书信这种通讯方式可能都比较陌生了。可别觉得书信是一个过时的东西,它可是80后的情怀,90后的回忆,00后的新宠,是经典的代名词。... 查看详情

harmonyos-基于arkui(js)实现黑白翻棋小游戏(代码片段)

作者:苏亚雯前言本人经过一段HarmonyOS的学习,运用所学的知识,制作了一个黑白翻棋的游戏,来检验自己的学习成果。本文详细讲述了黑白翻棋的编写思路,内含详细解释,有兴趣的小伙伴可以自己动手来制作一个属于自己的... 查看详情

#打卡不停更#ffh浅析ability框架中stage模型与fa模型的差异(代码片段)

...能力的抽象,也是应用程序的基本组成单元。OpenHarmony与HarmonyOS的应用程序APP由一个或多个Hap包组成,每个Hap可以包含一个或多个Ability。Ability框架模型具有两种形态,FA模型以及Stage模型:FA模型:OpenHarmonyAPI8 查看详情

基于stm32的串口收发讲解(hal库)#打卡不停更#(代码片段)

(基于STM32的串口收发程序(HAL库))介绍串口(UART通用异步收发器,TTL)通讯是一种设备间的串行全双工通讯方式。由于UART是异步传输,没有传输同步时钟,为了保证数据的正确性,UART采用16倍数据波特率的时钟进行采样。因为... 查看详情

openharmony/harmonyos的arkui的类web范式开发详解(代码片段)

一.OpenHarmony/HarmonyOS的ArkUI的类Web范式开发1.1类Web范式~三件套开发基于JS扩展的类****Web开发范****式的方舟开发框架包括应用层(Application)、前端框架层(Framework)、引擎层(Engine)、平台适配层(PortingLayer)JSUI框架采用类HTML和... 查看详情

arkui新能力,助力应用开发更便捷

...xff0c;并能在多种设备上实现生动而流畅的用户体验。随着HarmonyOS3.1版本的发布,ArkUI也新增许多能力,助力应用开发更便携。ArkUI框架新增能力概览ArkUI 查看详情

openharmony-基于arkui(ts)开发颜色选择器(代码片段)

...基于TS扩展的声明式开发范式,因为OpenHarmony的API相对于HarmonyOS的API,功能上比较完善和成熟的,有些新的技术也早早接触到,所以本项目直接使用OpenHarmonySDK开发。工具版本:DevEcoStudio3.0Beta4SDK版本:3. 查看详情

#打卡不停更#ffhopenharmony学生挑战赛分享-少儿语言教育app(代码片段)

Openharmony学生挑战赛经验分享前言本次参赛的项目是基于openHarmony开发的北向应用-少儿语言文化教育APP。从项目成立到初版成型再到参加比赛,这一路上遇到了不少困难,我也从团队协作、产品迭代、技术等方面学到了很多宝贵... 查看详情

#打卡不停更#智能喂食器(代码片段)

一、介绍​随着人们生活方式的不断改变,宠物猫在许多家庭中占有重要的地位,其凭借独立的个性和易于打理的饲养方式,成为当下上班族喜欢的宠物之一,人们更是把宠物猫和狗作为家庭的重要成员。有铲屎官表示,每月在... 查看详情

#打卡不停更#-openharmony/docs开发入门(代码片段)

作者:朱子道杨成前言不管是作为软件开发的爱好者还是已经从事软件开发这个行业的从业者,对于接触一种全新的系统OpenHarmony。学习OpenHarmony,需要清楚OpenHarmony这个系统是什么,能干什么,今日分享从设备开发和应用开发两... 查看详情

#打卡不停更#三方库移植之napi开发[2]c/c++与js的数据类型转换(代码片段)

在《三方库移植之NAPI开发[1]—HelloOpenHarmonyNAPI》通过一个HelloOpenHarmonyNAPI样例讲述了NPAI接口开发基础知识。本文在其基础上修改hellonapi.cpp文件,介绍JS类型和C/C++数据类型之间的转换。开发基于最新的OpenHarmony3.2Beta3版本及其对应... 查看详情