产品手记 鸿蒙 Next 实战: 电子木鱼

zero_dev(ZERO开发) · 2024年10月08日 · 150 次阅读

前言

正所谓:Hello Word 是程序员学任何一门语言的第一个程序实践。这其实也是一个不错的正反馈,那如何让学习鸿蒙 Next 更有成就感呢?下面就演示一下从零开发一个鸿蒙 Next 版的电子木鱼,主打就是一个抽象!

92210410404e8ff27918f02e3d85cf17_up-0e821fc204618a9429123f0a852e11df614.png

实现要点

  1. 页面布局
  2. 木鱼点击
  3. 木鱼音效
  4. 动画特效
  5. 自定义弹窗

39c305f85ef328c3890d44df61de3f9e_up-2bbe0ef0a0f3df2842362470e5921f57641.jpg

开始实践

页面布局

ArkTS 定义了声明式 UI 描述、自定义组件和动态扩展 UI 元素的能力,配合 ArkUI 开发框架中的系统组件及其相关的事件方法、属性方法等共同构成 UI 开发的主体。我们下面要完成的主要是一个木鱼和设置按钮、自动按钮。

build() {

Column() {
  HdNav({ title: '电子木鱼', showRightIcon: false, iconColor: $r('app.color.white'), titleColor: '#ffffff' })

  Row() {
    Text(this.woodenType[this.type] + ':'+ this.score).fontSize(22).fontColor("#ffffff").width('100%').textAlign(TextAlign.Center)
  }.width("100%").height("8%")

  Row() {
    Image($r('app.media.setting')).width(25).height(25).margin(16).onClick(() => {
      if (this.dialogController != null) {
        this.dialogController.open()
      }
    })
  }.width('100%')

  Row() {
    Image($r('app.media.foreground')).width(40).height(40).margin({left:8,top:5})
  }.width('100%')
  .onClick(() => {
    this.handlePopup = !this.handlePopup
  })
  .bindPopup(this.handlePopup, {
    message: '数据统计功能,正在完善中~',
  })

  Row() {
    if (this.isPresent) {
      Text(this.woodenType[this.type] + ': ' + this.woodenFishNum).fontSize(16).fontColor("#ffffff").width('100%').textAlign(TextAlign.Center)
        .transition(this.effect)
    }
  }.width('100%').height('25%')
  .alignItems(VerticalAlign.Top)

  Row() {
    Image($r('app.media.muyu'))
      .width(this.isZoomed == true ? this.targetWidth * 1.2 : this.targetWidth * 1)
      .height(this.isZoomed == true ? this.targetHeight * 1.2 : this.targetHeight * 1)
  }
  .width('100%')
  .height('25%')
  .alignItems(VerticalAlign.Center)
  .justifyContent(FlexAlign.Center)

  Row() {
    Toggle({ type: ToggleType.Switch })
      .onChange((isOn: boolean) => {
        if(isOn) {
          promptAction.showToast({ message: 'auto is on.' })
        } else {
          promptAction.showToast({ message: 'auto is off.' })
        }
      })

    Text('自动' + this.woodenType[this.type]).fontSize(18).fontColor('#ffffff').height(40).margin({left: 10})

  }.width('100%').height('10%').justifyContent(FlexAlign.Center)

}
.height("100%")
.backgroundColor('rgba(0, 0, 0, 1.00)')

}

木鱼点击

木鱼是一张图片,也就是给该图绑定一个点击事件,点击一次有三个动作需要执行:

  • 木鱼有放大的效果
  • 有类似功德文字的飘动
  • 功德数值的累加

而点击的时候要看到实时的效果,所以可以声明三个状态,通过 State 的修改,从而驱动 UI 更新,以下的 animateTo 是给域名的放大添加的一个平滑效果。

// 积分
@State score: number = 0
// 积分文字
@State isPresent: boolean = false
// 木鱼是否放大
@State isZoomed: boolean = false

// 木鱼UI
Image($r('app.media.muyu'))
.width(this.isZoomed == true ? this.targetWidth * 1.2 : this.targetWidth * 1)
.height(this.isZoomed == true ? this.targetHeight * 1.2 : this.targetHeight * 1)
.onClick((event) => {
animateTo({ curve: curves.springMotion() }, () => {
  this.isZoomed = !this.isZoomed;

  if (this.isZoomed == true) {
    this.isPresent = true;
    this.score += this.woodenFishNum;
    this.onClickPlay();
  }
})

// 定时缩小/定时文字消失
setTimeout(() => {this.isZoomed = false;}, 50);
setTimeout(() => {this.isPresent = false}, 600);
})

木鱼音效

木鱼音效是点击时的咚咚的声音,这里就要使用到 HarmonyOS Next 的音频服务。这里需要注意一点,项目运行预览无法播放,一定要模拟器或真机才可以调试音频的播放效果。

a50716261bdfd14746bf196b89a808b9_up-957f2f2082d91c8fd3f7bdcd25ac19a7577.png

// 销毁音效工具
  onClickDestroy= ()=>{
    AudioMgr.Ins().destroy();
    console.log('audio', 'destroy');
  }

  // 初始化音效工具
  onClickInit = ()=>{
    AudioMgr.Ins().init();
    console.log('audio', 'init');
  }

  // 播放指定音效
  onClickPlay = ()=>{
    AudioMgr.Ins().play();
    console.log('audio', 'playing');
  }

e095d6c6c7fd61ee09ad2e6750147361_up-bb7dc3e97e316f453c7f6a72aec07052dae.png

动画特效

这里的动画效果主要是点击木鱼,从下网上飘出一个文字然后消失的特效。在鸿蒙中可以通过 TransitionEffect 方法添加效果,首先创建特效,然后再文字上挂载。

// 上移入场特效
  private effect: object =
    TransitionEffect.OPACITY
      // 初始正常大小// 假设动画持续时间为500ms
      .combine(TransitionEffect.scale({ x: 1, y: 1 }).animation({ curve: curves.springMotion(0.6, 1.2), duration: 0 }))
       // 向上平移150单位// 与上一步同时开始
      .combine(TransitionEffect.translate({ x: 0, y: 400 }).animation({ curve: curves.springMotion(0.6, 1.2), duration: 10000, delay: 50 }))
       // 淡出至完全透明// 在平移结束后开始淡出
      .combine(TransitionEffect.opacity(0).animation({ curve: curves.springMotion(0.6, 1.2), duration: 1000, delay: 0 }));

a38c10c34fdb93be33ba7ca5f1e4bd61_up-dbe42f5c89bf43d36dcc62ab8459986682a.png

自定义弹窗

经过前面布局,事件绑定,音效播放,一个简单的电子木鱼其实已经完成了。但是为了增添趣味和后期扩展,这里再加一个设置功能,通过按钮打开配置项弹窗,设置包括:

  • 类型选项 (功德、财运、桃花运等)
  • 音效选项 (各种解压的音效素材)
  • 皮肤管理 (木鱼的 UI 界面设置)
  • 数值修改 (对展示的累加数值做任意修改)
  • 其他 (是否关闭音效,是否自动点击等)
// 弹窗层(UI开发-组件-自定义弹窗)
@CustomDialog
struct SettingDialog {
  controller?: CustomDialogController

  // 父子组件双向同步,文档见 https://developer.huawei.com/consumer/cn/doc/harmonyos-guides-V5/arkts-link-V5
  @Link woodenFishType: number

  // 木鱼敲击的数值
  @Link woodenFishNum: number

  build() {
    Column() {

      Row() {
        Text('愿望:').fontSize(17).fontWeight(600)
        Radio({ value: '功德', group: 'word' }).checked(true).onChange((isChecked: boolean) => {
          if(isChecked) {
            this.woodenFishType = 0
          }
        })
        Text('功德').fontSize(15)
        Radio({ value: '财富', group: 'word' }).onChange((isChecked: boolean) => {
          if(isChecked) {
            this.woodenFishType = 1
          }
        })
        Text('财富').fontSize(15)
        Radio({ value: '桃花运', group: 'word' }).onChange((isChecked: boolean) => {
          if(isChecked) {
            this.woodenFishType = 2
          }
        })
        Text('桃花运').fontSize(15)
      }
      .width('100%')
      .margin({bottom: 12})
      .justifyContent(FlexAlign.Start)

      Row() {
        Text('数值:').fontSize(16).fontWeight(600)
        TextInput({text:'1'}).type(InputType.Number).width(180).onChange((value: string) => {
          this.woodenFishNum = parseInt(value)
        })
      }
      .width('100%')
      .margin({bottom: 12})
      .justifyContent(FlexAlign.Start)

      Row() {
        Text('音效:').fontSize(16).fontWeight(600)
        Toggle({ type: ToggleType.Switch })
      }
      .width('100%')
      .margin({bottom: 12})
      .justifyContent(FlexAlign.Start)

      Row() {
        Text('皮肤:').fontSize(16).fontWeight(600)
        Radio({ value: '默认', group: 'skin' }).checked(true)
        Text('木鱼').fontSize(15)
        Radio({ value: '悟空', group: 'skin' })
        Text('黑悟空').fontSize(15)
        Radio({ value: '典韦', group: 'skin' })
        Text('典韦').fontSize(15)
      }
      .width('100%')
      .margin({bottom: 12})
      .justifyContent(FlexAlign.Start)

    }.padding({top: 28, left: 15})

  }
}

这里需要注意的是:父子组件的数据传递。因为自定义弹窗和木鱼是两个不同的组件,而点击弹窗中的比如类型切换或修改的数值,全部要更新到木鱼组件的展示当中。

当然鸿蒙也提供了 @Link 装饰器,用于与其父组件中的数据源共享相同的值,可以结合上面代码和下方截图参考其用法。

682dc27d391be1c5be1c3f2f088dc353_up-c59691bbee82b725b86786632bf238bb60e.png

写在后面

到这里,一个通用型的鸿蒙 Next 版电子木鱼就完成了。不管是组件交互还是布局都还好,唯一让我觉得不适应的是动画特效。 如果用这种方式实现电子烟花肯定不行,所以下次将换一种方法快速实现烟花秀,以及页面间的跳转,待更新~

暂无回复。
需要 登录 后方可回复, 如果你还没有账号请 注册新账号