
【引言】
在鸿蒙NEXT开发中,九宫格抽奖是一个常见且有趣的应用场景。通过九宫格抽奖,用户可以随机获得不同奖品,增加互动性和趣味性。本文将介绍如何使用鸿蒙开发框架实现九宫格抽奖功能,并通过代码解析展示实现细节。
【环境准备】
• 操作系统:Windows 10
• 开发工具:DevEco Studio NEXT Beta1 Build Version: 5.0.3.806
• 目标设备:华为Mate60 Pro
• 开发语言:ArkTS
• 框架:ArkUI
• API版本:API 12
【思路】
本案例中的“九宫格随机”应用旨在模拟一个简单的抽奖场景,用户点击抽奖按钮后,程序会从预先定义好的九个奖品中随机选择一个作为最终奖品。整个应用采用了响应式编程模式,结合鸿蒙NEXT提供的组件化开发方式,实现了交互流畅、视觉效果良好的用户体验。
-
Prize类设计 应用首先定义了一个Prize类,用于表示奖品信息。该类使用了@ObservedV2装饰器,使得奖品属性(如标题、颜色、描述)的变化可以被自动追踪,从而实现UI的实时更新。构造函数允许创建具有特定属性值的奖品实例,便于后续管理。
-
MyPrizeUpdate结构组件 为了提供奖品信息的编辑功能,我们创建了MyPrizeUpdate结构组件。它通过接收外部传入的数据(当前选中的奖品索引、抽奖顺序数组及所有奖品的数组),构建了一个包含文本输入框的界面,用户可以在其中修改奖品的标题、描述和颜色。任何对这些属性的更改都会即时反映到对应的奖品对象上,并触发UI的相应更新。
-
LotteryPage入口组件LotteryPage是整个抽奖应用的核心组件,负责组织页面布局和处理用户交互逻辑。它初始化了一系列必要的状态变量,比如保存所有奖品的数组prizeArray、定义抽奖顺序的selectionOrder以及控制动画状态的isAnimating等。此外,该组件实现了抽奖过程的关键方法——startLottery(开始抽奖)、runAtConstantSpeed(匀速运行)和slowDown(减速),它们共同协作以模拟真实的抽奖体验。当用户点击抽奖按钮时,这些方法按照预定的速度模式依次调用,直到最终确定一个奖品为止。最后,通过弹出对话框的方式向用户展示抽奖结果。
-
UI布局与样式 在构建UI方面,应用充分利用了鸿蒙NEXT提供的布局容器(如Column、Row、Flex)和样式属性(如宽度、高度、边距、背景色、圆角、阴影),精心设计了每个奖品项的外观。特别地,对于抽奖按钮,不仅设置了独特的背景颜色,还在点击事件中添加了动画效果,增强了用户的参与感。同时,考虑到不同设备屏幕尺寸的差异,所有布局元素均采用相对单位进行设置,确保了应用在各种终端上的良好适配性。
-
动画与交互优化 为了让抽奖过程看起来更加生动有趣,应用引入了加速、匀速、减速三个阶段的动画效果,使选中的奖品项能够以逐渐加快然后缓慢停止的方式出现在用户面前。这种变化不仅增加了悬念感,也提升了整体的娱乐性。此外,通过对点击事件的监听和处理,确保了即使是在动画过程中,用户的交互也不会受到影响,保证了良好的用户体验。
【完整代码】
@ObservedV2
class Prize {
@Trace title: string
@Trace color: string
@Trace description: string
constructor(title: string, color: string, description: string = "") {
this.title = title
this.color = color
this.description = description
}
}
@Component
struct MyPrizeUpdate {
@Consume selectedIndex: number
@Consume private selectionOrder: number[]
@Consume private prizeArray: Prize[]
build() {
Column({ space: 20 }) {
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%')
.backgroundColor(Color.White)
}
}
@Entry
@Component
struct LotteryPage {
@Provide private selectedIndex: number = 0
private isAnimating: boolean = 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
@State isSheetVisible: boolean = false
startLottery(speed: number = 500) {
setTimeout(() => {
if (speed > 50) {
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) {
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)
}
build() {
Column() {
Flex({ wrap: FlexWrap.Wrap }) {
ForEach(this.prizeArray, (item: Prize, index: number) => {
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})
}
.height('100%')
.width('100%')
.backgroundColor("#ffb350")
}
}
- 1.
- 2.
- 3.
- 4.
- 5.
- 6.
- 7.
- 8.
- 9.
- 10.
- 11.
- 12.
- 13.
- 14.
- 15.
- 16.
- 17.
- 18.
- 19.
- 20.
- 21.
- 22.
- 23.
- 24.
- 25.
- 26.
- 27.
- 28.
- 29.
- 30.
- 31.
- 32.
- 33.
- 34.
- 35.
- 36.
- 37.
- 38.
- 39.
- 40.
- 41.
- 42.
- 43.
- 44.
- 45.
- 46.
- 47.
- 48.
- 49.
- 50.
- 51.
- 52.
- 53.
- 54.
- 55.
- 56.
- 57.
- 58.
- 59.
- 60.
- 61.
- 62.
- 63.
- 64.
- 65.
- 66.
- 67.
- 68.
- 69.
- 70.
- 71.
- 72.
- 73.
- 74.
- 75.
- 76.
- 77.
- 78.
- 79.
- 80.
- 81.
- 82.
- 83.
- 84.
- 85.
- 86.
- 87.
- 88.
- 89.
- 90.
- 91.
- 92.
- 93.
- 94.
- 95.
- 96.
- 97.
- 98.
- 99.
- 100.
- 101.
- 102.
- 103.
- 104.
- 105.
- 106.
- 107.
- 108.
- 109.
- 110.
- 111.
- 112.
- 113.
- 114.
- 115.
- 116.
- 117.
- 118.
- 119.
- 120.
- 121.
- 122.
- 123.
- 124.
- 125.
- 126.
- 127.
- 128.
- 129.
- 130.
- 131.
- 132.
- 133.
- 134.
- 135.
- 136.
- 137.
- 138.
- 139.
- 140.
- 141.
- 142.
- 143.
- 144.
- 145.
- 146.
- 147.
- 148.
- 149.
- 150.
- 151.
- 152.
- 153.
- 154.
- 155.
- 156.
- 157.
- 158.
- 159.
- 160.
- 161.
- 162.
- 163.
- 164.
- 165.
- 166.
- 167.
- 168.
- 169.
- 170.
- 171.
- 172.
- 173.
- 174.
- 175.
- 176.
- 177.
- 178.
- 179.
- 180.
- 181.
- 182.
- 183.
- 184.
- 185.
- 186.
- 187.