鸿蒙NEXT开发案例:九宫格随机

news2025/3/1 13:11:12

【引言】

在鸿蒙NEXT开发中,九宫格抽奖是一个常见且有趣的应用场景。通过九宫格抽奖,用户可以随机获得不同奖品,增加互动性和趣味性。本文将介绍如何使用鸿蒙开发框架实现九宫格抽奖功能,并通过代码解析展示实现细节。

【环境准备】

• 操作系统:Windows 10

• 开发工具:DevEco Studio NEXT Beta1 Build Version: 5.0.3.806

• 目标设备:华为Mate60 Pro

• 开发语言:ArkTS

• 框架:ArkUI

• API版本:API 12

【思路】

本案例中的“九宫格随机”应用旨在模拟一个简单的抽奖场景,用户点击抽奖按钮后,程序会从预先定义好的九个奖品中随机选择一个作为最终奖品。整个应用采用了响应式编程模式,结合鸿蒙NEXT提供的组件化开发方式,实现了交互流畅、视觉效果良好的用户体验。

1. Prize类设计 应用首先定义了一个Prize类,用于表示奖品信息。该类使用了@ObservedV2装饰器,使得奖品属性(如标题、颜色、描述)的变化可以被自动追踪,从而实现UI的实时更新。构造函数允许创建具有特定属性值的奖品实例,便于后续管理。

2. MyPrizeUpdate结构组件 为了提供奖品信息的编辑功能,我们创建了MyPrizeUpdate结构组件。它通过接收外部传入的数据(当前选中的奖品索引、抽奖顺序数组及所有奖品的数组),构建了一个包含文本输入框的界面,用户可以在其中修改奖品的标题、描述和颜色。任何对这些属性的更改都会即时反映到对应的奖品对象上,并触发UI的相应更新。

3. LotteryPage入口组件LotteryPage是整个抽奖应用的核心组件,负责组织页面布局和处理用户交互逻辑。它初始化了一系列必要的状态变量,比如保存所有奖品的数组prizeArray、定义抽奖顺序的selectionOrder以及控制动画状态的isAnimating等。此外,该组件实现了抽奖过程的关键方法——startLottery(开始抽奖)、runAtConstantSpeed(匀速运行)和slowDown(减速),它们共同协作以模拟真实的抽奖体验。当用户点击抽奖按钮时,这些方法按照预定的速度模式依次调用,直到最终确定一个奖品为止。最后,通过弹出对话框的方式向用户展示抽奖结果。

4. UI布局与样式 在构建UI方面,应用充分利用了鸿蒙NEXT提供的布局容器(如Column、Row、Flex)和样式属性(如宽度、高度、边距、背景色、圆角、阴影),精心设计了每个奖品项的外观。特别地,对于抽奖按钮,不仅设置了独特的背景颜色,还在点击事件中添加了动画效果,增强了用户的参与感。同时,考虑到不同设备屏幕尺寸的差异,所有布局元素均采用相对单位进行设置,确保了应用在各种终端上的良好适配性。

5. 动画与交互优化 为了让抽奖过程看起来更加生动有趣,应用引入了加速、匀速、减速三个阶段的动画效果,使选中的奖品项能够以逐渐加快然后缓慢停止的方式出现在用户面前。这种变化不仅增加了悬念感,也提升了整体的娱乐性。此外,通过对点击事件的监听和处理,确保了即使是在动画过程中,用户的交互也不会受到影响,保证了良好的用户体验。

【完整代码】

// 定义一个可观察的Prize类,用于表示奖品信息。
@ObservedV2
class Prize {
  @Trace title: string // 奖品标题属性,使用@Trace进行追踪以便响应式更新UI
  @Trace color: string // 奖品颜色属性
  @Trace description: string // 奖品描述属性

  // 构造函数,用来初始化新的奖品实例
  constructor(title: string, color: string, description: string = "") {
    this.title = title // 设置奖品标题
    this.color = color // 设置奖品颜色
    this.description = description // 设置奖品描述,默认为空字符串
  }
}

// 定义MyPrizeUpdate结构组件,用于显示和编辑选中的奖品信息
@Component
struct MyPrizeUpdate {
  @Consume selectedIndex: number // 当前选中的奖品索引
  @Consume private selectionOrder: number[] // 保存抽奖顺序的数组
  @Consume private prizeArray: Prize[] // 保存所有奖品的数组

  build() {
    Column({ space: 20 }) { // 创建列布局容器,设置子元素之间的间距为20px
      Row() { // 创建行布局容器
        Text('标题:') // 显示“标题”文本
        TextInput({ text: this.prizeArray[this.selectionOrder[this.selectedIndex%this.selectionOrder.length]].title })
          .width('300lpx') // 设置输入框宽度
          .onChange((value) => { // 监听输入框内容变化
            this.prizeArray[this.selectionOrder[this.selectedIndex%this.selectionOrder.length]].title = value // 更新奖品标题
          })
      }
      Row() {
        Text('描述:')
        TextInput({
          text: `${this.prizeArray[this.selectionOrder[this.selectedIndex%this.selectionOrder.length]].description}`
        }).width('300lpx').onChange((value) => { // 同上,但针对奖品描述
          this.prizeArray[this.selectionOrder[this.selectedIndex%this.selectionOrder.length]].description = value
        })
      }
      Row() {
        Text('颜色:')
        TextInput({
          text: `${this.prizeArray[this.selectionOrder[this.selectedIndex%this.selectionOrder.length]].color}`
        }).width('300lpx').onChange((value) => { // 同上,但针对奖品颜色
          this.prizeArray[this.selectionOrder[this.selectedIndex%this.selectionOrder.length]].color = value
        })
      }
    }
    .justifyContent(FlexAlign.Start) // 设置内容左对齐
    .padding(40) // 设置内边距
    .width('100%') // 设置宽度为100%
    .backgroundColor(Color.White) // 设置背景颜色为白色
  }
}

// 定义抽奖页面入口组件
@Entry
@Component
struct LotteryPage {
  @Provide private selectedIndex: number = 0 // 提供当前选中的索引,初始值为0
  private isAnimating: boolean = false // 标记是否正在进行动画,初始值为false
  @Provide private selectionOrder: number[] = [0, 1, 2, 5, 8, 7, 6, 3] // 定义抽奖顺序
  private cellWidth: number = 200 // 单元格宽度
  private baseMargin: number = 10 // 单元格边距
  @Provide private prizeArray: Prize[] = [
    new Prize("红包", "#ff9675", "10元"), // 初始化奖品数组,创建各种奖品对象
    new Prize("话费", "#ff9f2e", "5元"),
    new Prize("红包", "#8e7fff", "50元"),
    new Prize("红包", "#48d1ea", "30元"),
    new Prize("开始抽奖", "#fffdfd"), // 抽奖按钮,没有具体奖品描述
    new Prize("谢谢参与", "#5f5f5f"),
    new Prize("谢谢参与", "#5f5f5f"),
    new Prize("超市红包", "#5f5f5f", "100元"),
    new Prize("鲜花", "#75b0fe"),
  ]
  private intervalID: number = 0 // 定时器ID,用于控制抽奖速度
  @State isSheetVisible: boolean = false // 控制底部弹出表单的可见性

  // 开始抽奖逻辑
  startLottery(speed: number = 500) {
    setTimeout(() => { // 设置延时执行
      if (speed > 50) { // 如果速度大于50,则递归调用startLottery以逐渐加速
        speed -= 50
        this.startLottery(speed)
      } else {
        this.runAtConstantSpeed() // 达到最高速度后进入匀速阶段
        return
      }
      this.selectedIndex++ // 每次调用时更新选中索引
    }, speed)
  }

  // 以恒定速度运行抽奖
  runAtConstantSpeed() {
    let speed = 40 + Math.floor(Math.random() * this.selectionOrder.length) // 随机生成一个速度值
    clearInterval(this.intervalID) // 清除之前的定时器
    this.intervalID = setInterval(() => { // 设置新的定时器来更新选中索引
      if (this.selectedIndex >= speed) { // 如果选中索引达到速度值,停止并进入减速阶段
        clearInterval(this.intervalID)
        this.slowDown()
        return
      }
      this.selectedIndex++
    }, 50)
  }

  // 减速逻辑
  slowDown(speed = 50) {
    setTimeout(() => { // 设置延时执行
      if (speed < 500) { // 如果速度小于500,则递归调用slowDown以逐渐减速
        speed += 50
        this.slowDown(speed)
      } else {
        this.selectedIndex %= this.selectionOrder.length // 确保索引在有效范围内
        let index = this.selectionOrder[this.selectedIndex] // 获取最终选中的奖品索引
        this.isAnimating = false // 动画结束
        this.getUIContext().showAlertDialog({ // 显示结果对话框
          title: '结果',
          message: `${this.prizeArray[index].title}${this.prizeArray[index].description}`, // 显示奖品信息
          confirm: {
            defaultFocus: true,
            value: '我知道了', // 确认按钮文本
            action: () => {} // 点击确认后的操作
          },
          alignment: DialogAlignment.Center,
        });
        return
      }
      this.selectedIndex++
    }, speed)
  }

  // 构建UI方法
  build() {
    Column() { // 使用Column布局容器
      Flex({ wrap: FlexWrap.Wrap }) { // 使用弹性布局,允许换行
        ForEach(this.prizeArray, (item: Prize, index: number) => { // 遍历奖品数组,创建每个奖品的UI
          Column() { // 使用Column布局容器为每个奖品项
            Text(`${item.title}`) // 显示奖品标题
              .fontColor(index == 4 ? Color.White : item.color) // 设置字体颜色,对于抽奖按钮特殊处理
              .fontSize(16)
            Text(`${item.description}`) // 显示奖品描述
              .fontColor(index == 4 ? Color.White : item.color) // 设置字体颜色
              .fontSize(20)
          }
          .clickEffect({ level: ClickEffectLevel.LIGHT, scale: 0.8 }) // 添加点击效果
          .onClick(() => { // 处理点击事件
            if (this.isAnimating) { // 如果正在动画中,忽略点击
              return
            }
            if (index == 4) { // 如果点击的是抽奖按钮,开始抽奖
              this.isAnimating = true
              this.startLottery()
            } else {
              for (let i = 0; i < this.selectionOrder.length; i++) {
                if (this.selectionOrder[i] == index) {
                  this.selectedIndex = i // 更新选中索引到对应位置
                }
              }
            }
          })
          .alignItems(HorizontalAlign.Center) // 设置水平居中对齐
          .justifyContent(FlexAlign.Center) // 设置垂直居中对齐
          .width(`${this.cellWidth}lpx`) // 设置单元格宽度
          .height(`${this.cellWidth}lpx`) // 设置单元格高度
          .margin(`${this.baseMargin}lpx`) // 设置单元格边距
          .backgroundColor(index == 4 ? "#ff5444" : // 抽奖按钮背景颜色特殊处理
            (this.selectionOrder[this.selectedIndex % this.selectionOrder.length] == index ? Color.Gray : Color.White))
          .borderRadius(10) // 设置圆角
          .shadow({ // 设置阴影效果
            radius: 10,
            color: "#f98732",
            offsetX: 0,
            offsetY: 20
          })
        })
      }.width(`${this.cellWidth * 3 + this.baseMargin * 6}lpx`) // 设置整体宽度
      .margin({ top: 30 }) // 设置顶部边距
      MyPrizeUpdate().margin({top:20}) // 插入MyPrizeUpdate组件,并设置其上边距
    }
    .height('100%') // 设置高度为100%
    .width('100%') // 设置宽度为100%
    .backgroundColor("#ffb350") // 设置页面背景颜色
  }
}

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/2259573.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

【efinance一个2k星的库】

efinance 是一个可以快速获取基金、股票、债券、期货数据的 Python 库&#xff0c;回测以及量化交易的好帮手 但没有等比复权的&#xff0c;不用。 import efinance as ef ef.stock.get_quote_history(510880,fqt2)

协同办公软件新升级:细节优化,让办公更简单

细节决定成败&#xff0c;企业酷信协同办公系统通过贴近客户实际需求的一系列改进和创新&#xff0c;在技术架构、系统结构、管理理念和使用性能上&#xff0c;都达到了国内先进水平&#xff0c;同时具备独特的优势。让我们看看企业酷信是如何通过这些细节提升&#xff0c;为企…

【AI知识】有监督学习分类任务之支持向量机

1.支持向量机概念 支持向量机&#xff08;Support Vector Machine, SVM&#xff09; 是一种有监督学习算法&#xff0c;主要用于分类任务&#xff08;也可用于回归任务&#xff0c;即支持向量回归&#xff0c;SVR&#xff09;。SVM的核心思想是找到一个最优的超平面&#xff0…

MySQL有哪些高可用方案?

大家好&#xff0c;我是锋哥。今天分享关于【MySQL有哪些高可用方案?】面试题。希望对大家有帮助&#xff1b; MySQL有哪些高可用方案? 1000道 互联网大厂Java工程师 精选面试题-Java资源分享网 MySQL 高可用方案旨在确保数据库系统的高可靠性、低宕机时间、以及在硬件故障…

利用Docker分层构建优化镜像大小

合适docker镜像文件大小不仅影响容器启动效率&#xff0c;也影响资源占用效率。本文介绍如何利用分层方式构建docker镜像&#xff0c;采用多种方式避免镜像文件太大而影响性能。 Docker 镜像大小优化的重要性 资源利用效率 较小的镜像文件在存储和传输过程中占用更少的空间和带…

threejs——无人机概念切割效果

主要技术采用着色器的切割渲染,和之前写的风车可视化的文章不同,这次的切割效果是在着色器的基础上实现的,并新增了很多可调节的变量,兄弟们,走曲儿~ 线上演示地址,点击体验 源码下载地址,点击下载 正文 从图中大概可以看出以下信息,一个由线组成的无人机模型,一个由…

我的AI工具箱Tauri版-SpiderYtDlp油管视频多功能下载

本教程基于自研的AI工具箱Tauri版进行SpiderYtDlp油管视频多功能下载。 SpiderYtDlp油管视频多功能下载 是一款基于自研AI工具箱的全新解决方案&#xff0c;专为满足多场景需求的用户设计。工具集成了便捷的链接直接下载功能和强大的关键词模糊搜索下载功能&#xff0c;赋能创…

PyCharm如何导入库( 包 )

目录 1.在主界面中导库 2.用设置->项目安装库 2.1.使用右上方按钮 2.2.使用右下方Python解释器 3.使用左下角终端导库 1.在主界面中导库 在主界面输入导库后等待一会儿&#xff0c;会在那一行出现一个红色灯。 图1 红色灯 我们点击红色灯&#xff0c;会出现 图2 错误选…

Bananna Pi开源社区联合矽昌通信打造开源的低成本Wifi5路由器

香蕉派 BPI-Wifi5 路由器采用矽昌SF19A2890S2芯片方案设计。它是一款高性能无线路由器&#xff0c;适用于小微企业、家庭和其他网络环境。Banana Pi开源社区提供整体解决方案。所有代码开源&#xff0c;用户可以在上面自由开发自己的应用。 Banana Pi wifi5 路由器github代码: …

Spring Boot教程之二十五: 使用 Tomcat 部署项目

Spring Boot – 使用 Tomcat 部署项目 Spring Boot 是一个基于微服务的框架&#xff0c;在其中创建可用于生产的应用程序只需很少的时间。Spring Boot 建立在 Spring 之上&#xff0c;包含 Spring 的所有功能。如今&#xff0c;它正成为开发人员的最爱&#xff0c;因为它是一个…

scale index的计算

scale index定义 基本实现 需要注意&#xff0c;scale index的提出者分别构建了MATLAB和R语言的实现方式。 但是&#xff0c;需要注意&#xff0c;经过我向作者求证。 MATLAB编写的代码已经“过时了”&#xff0c;为了拥抱时代&#xff0c;作者构建了R语言包&#xff0c;名称为…

虚幻5描边轮廓材质

很多游戏内都有这种描边效果&#xff0c;挺实用也挺好看的&#xff0c;简单复刻一下 效果演示&#xff1a; Linethickness可以控制轮廓线条的粗细 这样连完&#xff0c;然后放到网格体细节的覆层材质上即可 可以自己更改粗细大小和颜色

【Java Nio Netty】基于TCP的简单Netty自定义协议实现(万字,全篇例子)

基于TCP的简单Netty自定义协议实现&#xff08;万字&#xff0c;全篇例子&#xff09; 前言 有一阵子没写博客了&#xff0c;最近在学习Netty写一个实时聊天软件&#xff0c;一个高性能异步事件驱动的网络应用框架&#xff0c;我们常用的SpringBoot一般基于Http协议&#xff0…

Ubuntu 20.04LTS 系统离线安装5.7.44mysql数据库

Ubuntu 20.04LTS 系统离线安装5.7.44mysql数据库 环境下载 MySQL 5.7.44 包安装标题检查服务是否启动成功遇到的问题登陆&修改密码&远程访问 环境 操作系统&#xff1a;Ubuntu 20.04.4 LTS 数据库&#xff1a;MySQL 5.7.34 内核版本&#xff1a;x86_64&#xff08;amd…

后端-redis的使用

redis的服务端启动命令&#xff0c;打开redis的目录&#xff0c;输入cmd redis的客户端启动命令 设置redis密码 redis连接 指定ip地址的服务端,没设密码&#xff1a;redis-cli.exe -h localhost -p 6379 edis连接 指定ip地址的服务端,设置了密码&#xff1a;redis-cli.ex…

前端成长之路:CSS字体、文本属性和引入方式

本文主要介绍CSS的字体属性和文本属性&#xff0c;最后再介绍CSS在HTML中的引入方式。 CSS字体属性 CSS Fonts&#xff08;字体&#xff09;属性能用于定义字体系列属性&#xff0c;包括但不限于字体大小、粗细、字体样式等。 字体系列 在CSS中使用font-family属性定义文本…

基于windows环境使用nvm安装多版本nodejs

目录 前言 一、卸载node 二、nvm是什么&#xff1f; 三、nvm安装 1.官网下载 nvm 包 2. 安装nvm-setup.exe 3. 配置路径和下载镜像 4. 检查安装是否完成 四、 使用nvm安装node 五、修改npm默认镜像源为淘宝镜像 六、环境变量配置 1. 新建目录 2. 设置环境变量 七…

排序算法(2):选择排序

问题 排序 [30, 24, 5, 58, 18, 36, 12, 42, 39] 选择排序 选择排序每次从待排序序列中选出最小&#xff08;或最大&#xff09;的元素&#xff0c;将其放到序列的起始位置&#xff0c;然后&#xff0c;再从剩余未排序元素中继续寻找最小&#xff08;或最大&#xff09;元素…

009-jvm-对象相关的概念

#案例&#xff1a; 对象的创建过程 初始化默认值 成员变量显示赋值 构造代码块的初始化 构造器中的初始化 jvm

【硬件测试】基于FPGA的4FSK调制解调通信系统开发与硬件片内测试,包含信道模块,误码统计模块,可设置SNR

目录 1.算法仿真效果 2.算法涉及理论知识概要 3.Verilog核心程序 4.开发板使用说明和如何移植不同的开发板 5.完整算法代码文件获得 1.算法仿真效果 本文是之前写的文章: 《基于FPGA的4FSK调制解调系统,包含testbench,高斯信道模块,误码率统计模块,可以设置不同SNR》 的…