在移动掌握中,抽奖功能是一种常见且受迎接的交互风物,能够有用升迁用户粘性。本教程将指挥全球从零动手,稳固已毕一个九宫格抽奖成果自拍偷拍]藝校兩女生自慰裸聊視頻,妥贴HarmonyOS开发的入门者阅读。 Hongkongdoll videos 开发环境准备 DevEco Studio 5.0.3HarmonyOS Next API 15 最终成果预览 咱们将已毕一个经典的九宫格抽奖界面,包含以下中枢功能: 3×3网格布局展示奖品点击中间按钮启动抽奖高亮格子轮回移动的动画成果动态变速,模拟确实抽奖...
在移动掌握中,抽奖功能是一种常见且受迎接的交互风物,能够有用升迁用户粘性。本教程将指挥全球从零动手,稳固已毕一个九宫格抽奖成果自拍偷拍]藝校兩女生自慰裸聊視頻,妥贴HarmonyOS开发的入门者阅读。
Hongkongdoll videos开发环境准备
DevEco Studio 5.0.3HarmonyOS Next API 15
最终成果预览
咱们将已毕一个经典的九宫格抽奖界面,包含以下中枢功能:
3×3网格布局展示奖品点击中间按钮启动抽奖高亮格子轮回移动的动画成果动态变速,模拟确实抽奖经由预设中奖阻隔的展示
已毕才智
才智一:创建基本结构和数据模子
最初,咱们需要创建一个基础页面结构和界说数据模子。通过界说奖品的数据结构,为后续的九宫格布局作念准备。
// 界说奖品项的接口interface PrizeItem { id: number name: string icon: ResourceStr color: string}@Entry@Componentstruct LuckyDraw { // 基本页面结构 build() { Column() { Text('行运抽奖') .fontSize(24) .fontColor(Color.White) } .width('100%') .height('100%') .backgroundColor('#121212') }}
在这一步,咱们界说了PrizeItem接口来表率奖品的数据结构,并创建了一个基本的页面结构,只包含一个标题。
才智二:创建奖品数据和情景搞定
接下来,咱们添加具体的奖品数据,并界说抽奖功能所需的情景变量。
@Entry@Componentstruct LuckyDraw { // 界说奖品数组 @State prizes: PrizeItem[] = [ { id: 1, name: '谢谢参与', icon: $r('app.media.startIcon'), color: '#FF9500' }, { id: 2, name: '10积分', icon: $r('app.media.startIcon'), color: '#34C759' }, { id: 3, name: '优惠券', icon: $r('app.media.startIcon'), color: '#007AFF' }, { id: 8, name: '1元红包', icon: $r('app.media.startIcon'), color: '#FF3B30' }, { id: 0, name: '动手\n抽奖', icon: $r('app.media.startIcon'), color: '#FF2D55' }, { id: 4, name: '5元红包', icon: $r('app.media.startIcon'), color: '#5856D6' }, { id: 7, name: '免单券', icon: $r('app.media.startIcon'), color: '#E73C39' }, { id: 6, name: '50积分', icon: $r('app.media.startIcon'), color: '#38B0DE' }, { id: 5, name: '会员卡', icon: $r('app.media.startIcon'), color: '#39A5DC' }, ] // 现时高亮的奖品索引 @State currentIndex: number = -1 // 是否正在抽奖 @State isRunning: boolean = false // 中奖阻隔 @State result: string = '点击动手抽奖' build() { // 页面结构保握不变 }}
在这一步,咱们添加了以下实质:
创建了一个包含9个奖品的数组,每个奖品王人有id、称呼、图标和激情属性添加了三个情景变量:currentIndex:追踪现时高亮的奖品索引isRunning:标记抽奖是否正在进行result:纪录并暴露抽奖阻隔
才智三:已毕九宫格布局
当今咱们来已毕九宫格的基本布局,使用Grid组件和ForEach轮回遍历奖品数组。
build() { Column({ space: 30 }) { // 标题 Text('行运抽奖') .fontSize(24) .fontWeight(FontWeight.Bold) .fontColor(Color.White) // 阻隔暴露区域 Column() { Text(this.result) .fontSize(20) .fontColor(Color.White) } .width('90%') .padding(15) .backgroundColor('#0DFFFFFF') .borderRadius(16) // 九宫格抽奖区域 Grid() { ForEach(this.prizes, (prize: PrizeItem, index) => { GridItem() { Column() { if (index === 4) { // 中间的动手按钮 Button({ type: ButtonType.Capsule }) { Text(prize.name) .fontSize(18) .fontWeight(FontWeight.Bold) .textAlign(TextAlign.Center) .fontColor(Color.White) } .width('90%') .height('90%') .backgroundColor(prize.color) } else { // 平庸奖品格子 Image(prize.icon) .width(40) .height(40) Text(prize.name) .fontSize(14) .fontColor(Color.White) .margin({ top: 8 }) .textAlign(TextAlign.Center) } } .width('100%') .height('100%') .justifyContent(FlexAlign.Center) .alignItems(HorizontalAlign.Center) .backgroundColor(prize.color) .borderRadius(12) .padding(10) } }) } .columnsTemplate('1fr 1fr 1fr') .rowsTemplate('1fr 1fr 1fr') .columnsGap(10) .rowsGap(10) .width('90%') .aspectRatio(1) .backgroundColor('#0DFFFFFF') .borderRadius(16) .padding(10) } .width('100%') .height('100%') .justifyContent(FlexAlign.Center) .backgroundColor(Color.Black) .linearGradient({ angle: 135, colors: [ ['#121212', 0], ['#242424', 1] ] })}
在这一步,咱们已毕了以下实质:
创建了举座的页面布局,包括标题、阻隔暴露区域和九宫格区域使用 Grid 组件创建3×3的网格布局使用 ForEach 遍历奖品数组,为每个奖品创建一个格子凭证索引判断,为中间位置创建"动手抽奖"按钮,其他位置暴露奖品信息为每个格子开荒了合适的风物和背惬心
才智四:已毕高亮成果和点击事件
接下来,咱们要已毕格子的高亮成果,并添加点击事件处理。
build() { Column({ space: 30 }) { // 前边的代码保握不变... // 九宫格抽奖区域 Grid() { ForEach(this.prizes, (prize: PrizeItem, index) => { GridItem() { Column() { if (index === 4) { // 中间的动手按钮 Button({ type: ButtonType.Capsule }) { Text(prize.name) .fontSize(18) .fontWeight(FontWeight.Bold) .textAlign(TextAlign.Center) .fontColor(Color.White) } .width('90%') .height('90%') .backgroundColor(prize.color) .onClick(() => this.startLottery()) // 添加点击事件 } else { // 平庸奖品格子 Image(prize.icon) .width(40) .height(40) Text(prize.name) .fontSize(14) .fontColor(index === this.currentIndex ? prize.color : Color.White) // 高亮时修改翰墨激情 .margin({ top: 8 }) .textAlign(TextAlign.Center) } } .width('100%') .height('100%') .justifyContent(FlexAlign.Center) .alignItems(HorizontalAlign.Center) .backgroundColor(index === this.currentIndex && index !== 4 ? Color.White : prize.color) // 高亮时切换背惬心 .borderRadius(12) .padding(10) .animation({ // 添加动画成果 duration: 200, curve: Curve.EaseInOut }) } }) } // Grid的其他属性保握不变... } // Column的属性保握不变...}// 添加动手抽奖的空步调startLottery() { // 鄙人一步已毕}
在这一步,咱们:
为中间的"动手抽奖"按钮添加了点击事件处理步调startLottery()已毕了格子高亮成果:当格子被选中时(index === this.currentIndex),背惬心变为白色,翰墨激情变为奖品激情添加了动画成果,使高亮切换愈加平滑预界说了startLottery()步调,暂时为空已毕
才智五:已毕抽奖动画逻辑
当今咱们来已毕抽奖动画的中枢逻辑,包括轮回高亮、速率变化和阻隔戒指。
@Entry@Componentstruct LuckyDraw { // 前边的情景变量保握不变... // 添加动画戒指关系变量 private timer: number = 0 private speed: number = 100 private totalRounds: number = 30 private currentRound: number = 0 private targetIndex: number = 2 // 假定固定中奖"优惠券" // 动手抽奖 startLottery() { if (this.isRunning) { return // 防患类似点击 } this.isRunning = true this.result = '抽奖中...' this.currentRound = 0 this.speed = 100 // 启动动画 this.runLottery() } // 运行抽奖动画 runLottery() { if (this.timer) { clearTimeout(this.timer) } this.timer = setTimeout(() => { // 更新现时高亮的格子 this.currentIndex = (this.currentIndex + 1) % 9 if (this.currentIndex === 4) { // 跳过中间的"动手抽奖"按钮 this.currentIndex = 5 } this.currentRound++ // 增多快度变化,模拟降速成果 if (this.currentRound > this.totalRounds * 0.7) { this.speed += 10 // 大幅降速 } else if (this.currentRound > this.totalRounds * 0.5) { this.speed += 5 // 小幅降速 } // 阻隔条目判断 if (this.currentRound >= this.totalRounds && this.currentIndex === this.targetIndex) { // 抽奖阻隔 this.isRunning = false this.result = `恭喜取得: ${this.prizes[this.targetIndex].name}` } else { // 不息动画 this.runLottery() } }, this.speed) } // 组件阵一火时吊销定时器 aboutToDisappear() { if (this.timer) { clearTimeout(this.timer) this.timer = 0 } } // build步调保握不变...}
在这一步,咱们已毕了抽奖动画的中枢逻辑:
添加了动画戒指关系变量:timer:用于存储定时器IDspeed:戒指动画速率totalRounds:整个旋转的轮数currentRound:现时已旋转的轮数targetIndex:预设的中奖索引已毕了startLottery()步调:防患类似点击运行化抽奖情景调用runLottery()动手动画已毕了runLottery()步调:使用setTimeout创建轮回动画更新高亮格子的索引,并跳过中间的动手按钮凭证进程增多蔓延时辰,模拟降速成果凭证条目判断是否阻隔动画递归调用自己造成动画轮回添加了aboutToDisappear()生命周期步调,确保在组件阵一火时吊销定时器,幸免内存流露
圆善代码
终末,咱们对代码进行完善和优化,确保抽奖功能平方职责并升迁用户体验。
圆善的代码如下:
interface PrizeItem { id: number name: string icon: ResourceStr color: string}@Entry@Componentstruct LuckyDraw { // 界说奖品数组 @State prizes: PrizeItem[] = [ { id: 1, name: '谢谢参与', icon: $r('app.media.startIcon'), color: '#FF9500' }, { id: 2, name: '10积分', icon: $r('app.media.startIcon'), color: '#34C759' }, { id: 3, name: '优惠券', icon: $r('app.media.startIcon'), color: '#007AFF' }, { id: 8, name: '1元红包', icon: $r('app.media.startIcon'), color: '#FF3B30' }, { id: 0, name: '动手\n抽奖', icon: $r('app.media.startIcon'), color: '#FF2D55' }, { id: 4, name: '5元红包', icon: $r('app.media.startIcon'), color: '#5856D6' }, { id: 7, name: '免单券', icon: $r('app.media.startIcon'), color: '#E73C39' }, { id: 6, name: '50积分', icon: $r('app.media.startIcon'), color: '#38B0DE' }, { id: 5, name: '会员卡', icon: $r('app.media.startIcon'), color: '#39A5DC' }, ] // 现时高亮的奖品索引 @State currentIndex: number = -1 // 是否正在抽奖 @State isRunning: boolean = false // 中奖阻隔 @State result: string = '点击下方按钮动手抽奖' // 动画定时器 private timer: number = 0 // 动画速率戒指 private speed: number = 100 private totalRounds: number = 30 private currentRound: number = 0 // 预设中奖索引(不错凭证概率立时生成) private targetIndex: number = 2 // 假定固定中奖"优惠券" // 动手抽奖 startLottery() { if (this.isRunning) { return } this.isRunning = true this.result = '抽奖中...' this.currentRound = 0 this.speed = 100 // 启动动画 this.runLottery() } // 运行抽奖动画 runLottery() { if (this.timer) { clearTimeout(this.timer) } this.timer = setTimeout(() => { // 更新现时高亮的格子 this.currentIndex = (this.currentIndex + 1) % 9 if (this.currentIndex === 4) { // 跳过中间的"动手抽奖"按钮 this.currentIndex = 5 } this.currentRound++ // 增多快度变化,模拟降速成果 if (this.currentRound > this.totalRounds * 0.7) { this.speed += 10 } else if (this.currentRound > this.totalRounds * 0.5) { this.speed += 5 } // 阻隔条目判断 if (this.currentRound >= this.totalRounds && this.currentIndex === this.targetIndex) { // 抽奖阻隔 this.isRunning = false this.result = `恭喜取得: ${this.prizes[this.targetIndex].name}` } else { // 不息动画 this.runLottery() } }, this.speed) } // 组件阵一火时吊销定时器 aboutToDisappear() { if (this.timer) { clearTimeout(this.timer) this.timer = 0 } } build() { Column({ space: 30 }) { // 标题 Text('行运抽奖') .fontSize(24) .fontWeight(FontWeight.Bold) .fontColor(Color.White) // 阻隔暴露 Column() { Text(this.result) .fontSize(20) .fontColor(Color.White) } .width('90%') .padding(15) .backgroundColor('#0DFFFFFF') .borderRadius(16) // 九宫格抽奖区域 Grid() { ForEach(this.prizes, (prize: PrizeItem, index) => { GridItem() { Column() { if (index === 4) { // 中间的动手按钮 Button({ type: ButtonType.Capsule }) { Text(prize.name) .fontSize(18) .fontWeight(FontWeight.Bold) .textAlign(TextAlign.Center) .fontColor(Color.White) } .width('90%') .height('90%') .backgroundColor(prize.color) .onClick(() => this.startLottery()) } else { // 平庸奖品格子 Image(prize.icon) .width(40) .height(40) Text(prize.name) .fontSize(14) .fontColor(index === this.currentIndex && index !== 4 ? prize.color : Color.White) .margin({ top: 8 }) .textAlign(TextAlign.Center) } } .width('100%') .height('100%') .justifyContent(FlexAlign.Center) .alignItems(HorizontalAlign.Center) .backgroundColor(index === this.currentIndex && index !== 4 ? Color.White : prize.color) .borderRadius(12) .padding(10) .animation({ duration: 200, curve: Curve.EaseInOut }) } }) } .columnsTemplate('1fr 1fr 1fr') .rowsTemplate('1fr 1fr 1fr') .columnsGap(10) .rowsGap(10) .width('90%') .aspectRatio(1) .backgroundColor('#0DFFFFFF') .borderRadius(16) .padding(10) } .width('100%') .height('100%') .justifyContent(FlexAlign.Center) .backgroundColor(Color.Black) .linearGradient({ angle: 135, colors: [ ['#121212', 0], ['#242424', 1] ] }) .expandSafeArea() // 激情膨大到安全区域 }}
中枢见解领略
1. Grid组件
Grid组件是已毕九宫格布局的中枢,它具有以下进攻属性:
columnsTemplate:界说网格的列模板。'1fr 1fr 1fr'暗示三列等宽布局。rowsTemplate:界说网格的行模板。'1fr 1fr 1fr'暗示三行等高布局。columnsGap和rowsGap:开荒列和行之间的间距。aspectRatio:开荒宽高比,确保网格是正方形。
2. 动画已毕旨趣
抽奖动画的中枢是通过定时器和情景更新已毕的:
轮回高亮:通过setTimeout定时更新currentIndex情景,已毕格子的轮回高亮。动态速率:跟着轮回轮数的增多,渐渐增多蔓延时辰(this.speed += 10),已毕降速成果。阻隔条目:当得志两个条目时住手动画:已完成设定的总轮数(this.currentRound >= this.totalRounds)现时高亮的格子是狡计奖品(this.currentIndex === this.targetIndex)
3. 高亮成果
格子的高亮成果是通过条目风物已毕的:
.backgroundColor(index === this.currentIndex && index !== 4 ? Color.White : prize.color)
当格子被选中时(index === this.currentIndex),背惬心变为白色,翰墨激情变为奖品激情,产生对比明显的高亮成果。
4. 资源计帐
在组件阵一火时,咱们需要吊销定时器以幸免内存流露:
aboutToDisappear() { if (this.timer) { clearTimeout(this.timer) this.timer = 0 }}
进阶优化念念路
完成基本功能后,不错研讨以下优化宗旨:
1. 立时中奖阻隔
目下中奖阻隔是固定的,不错已毕一个立时算法,凭证概率分拨不同奖品:
// 凭证概率生成中奖索引generatePrizeIndex() { // 界说各奖品的概率权重 const weights = [50, 10, 5, 3, 0, 2, 1, 8, 20]; // 数字越梗概率越高 const totalWeight = weights.reduce((a, b) => a + b, 0); // 生建树时数 const random = Math.random() * totalWeight; // 凭证权重决定中奖索引 let currentWeight = 0; for (let i = 0; i < weights.length; i++) { if (i === 4) continue; // 跳过中间的"动手抽奖"按钮 currentWeight += weights[i]; if (random < currentWeight) { return i; } } return 0; // 默许复返第一个奖品}
2. 抽奖音效
添加音效不错升迁用户体验:
// 播放抽奖音效playSound(type: 'start' | 'running' | 'end') { // 凭证不同阶段播放不同音效}
3. 振动反映
在抽奖动手和阻隔时添加振动反映:
// 导入振动模块import { vibrator } from '@kit.SensorServiceKit';// 触发振动triggerVibration() { vibrator.vibrate(50); // 振动50毫秒}
4. 抽奖次数界限
添加抽奖次数界限和剩余次数暴露:
@State remainingTimes: number = 3; // 剩余抽奖次数startLottery() { if (this.isRunning 自拍偷拍]藝校兩女生自慰裸聊視頻