鸿蒙特效教程07-九宫格幸运抽奖 原创

开门的鸿蒙
发布于 2025-3-26 19:10
浏览
0收藏

鸿蒙特效教程07-九宫格幸运抽奖

在移动应用中,抽奖功能是一种常见且受欢迎的交互方式,能够有效提升用户粘性。本教程将带领大家从零开始,逐步实现一个九宫格抽奖效果,适合HarmonyOS开发的初学者阅读。

开发环境准备

  • DevEco Studio 5.0.3
  • HarmonyOS Next API 15

最终效果预览

我们将实现一个经典的九宫格抽奖界面,包含以下核心功能:

  • 3×3网格布局展示奖品
  • 点击中间按钮启动抽奖
  • 高亮格子循环移动的动画效果
  • 动态变速,模拟真实抽奖过程
  • 预设中奖结果的展示

鸿蒙特效教程07-九宫格幸运抽奖-鸿蒙开发者社区

实现步骤

步骤一:创建基本结构和数据模型

首先,我们需要创建一个基础页面结构和定义数据模型。通过定义奖品的数据结构,为后续的九宫格布局做准备。

// 定义奖品项的接口
interface PrizeItem {
  id: number
  name: string
  icon: ResourceStr
  color: string
}

@Entry
@Component
struct LuckyDraw {
  // 基本页面结构
  build() {
    Column() {
      Text('幸运抽奖')
        .fontSize(24)
        .fontColor(Color.White)
    }
    .width('100%')
    .height('100%')
    .backgroundColor('#121212')
  }
}
  • 1.
  • 2.
  • 3.
  • 4.
  • 5.
  • 6.
  • 7.
  • 8.
  • 9.
  • 10.
  • 11.
  • 12.
  • 13.
  • 14.
  • 15.
  • 16.
  • 17.
  • 18.
  • 19.
  • 20.
  • 21.
  • 22.
  • 23.

在这一步,我们定义了PrizeItem接口来规范奖品的数据结构,并创建了一个基本的页面结构,只包含一个标题。

步骤二:创建奖品数据和状态管理

接下来,我们添加具体的奖品数据,并定义抽奖功能所需的状态变量。

@Entry
@Component
struct 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() {
    // 页面结构保持不变
  }
}
  • 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.

在这一步,我们添加了以下内容:

  1. 创建了一个包含9个奖品的数组,每个奖品都有id、名称、图标和颜色属性
  2. 添加了三个状态变量:
    • 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]
    ]
  })
}
  • 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.

在这一步,我们实现了以下内容:

  1. 创建了整体的页面布局,包括标题、结果显示区域和九宫格区域
  2. 使用 Grid 组件创建3×3的网格布局
  3. 使用 ForEach 遍历奖品数组,为每个奖品创建一个格子
  4. 根据索引判断,为中间位置创建"开始抽奖"按钮,其他位置显示奖品信息
  5. 为每个格子设置了合适的样式和背景色

步骤四:实现高亮效果和点击事件

接下来,我们要实现格子的高亮效果,并添加点击事件处理。

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() {
  // 在下一步实现
}
  • 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.

在这一步,我们:

  1. 为中间的"开始抽奖"按钮添加了点击事件处理方法startLottery()
  2. 实现了格子高亮效果:
    • 当格子被选中时(index === this.currentIndex),背景色变为白色,文字颜色变为奖品颜色
    • 添加了动画效果,使高亮切换更加平滑
  3. 预定义了startLottery()方法,暂时为空实现

步骤五:实现抽奖动画逻辑

现在我们来实现抽奖动画的核心逻辑,包括循环高亮、速度变化和结果控制。

@Entry
@Component
struct 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方法保持不变...
}
  • 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.

在这一步,我们实现了抽奖动画的核心逻辑:

  1. 添加了动画控制相关变量:

    • timer:用于存储定时器ID
    • speed:控制动画速度
    • totalRounds:总共旋转的轮数
    • currentRound:当前已旋转的轮数
    • targetIndex:预设的中奖索引
  2. 实现了startLottery()方法:

    • 防止重复点击
    • 初始化抽奖状态
    • 调用runLottery()开始动画
  3. 实现了runLottery()方法:

    • 使用setTimeout创建循环动画
    • 更新高亮格子的索引,并跳过中间的开始按钮
    • 根据进度增加延迟时间,模拟减速效果
    • 根据条件判断是否结束动画
    • 递归调用自身形成动画循环
  4. 添加了aboutToDisappear()生命周期方法,确保在组件销毁时清除定时器,避免内存泄漏

完整代码

最后,我们对代码进行完善和优化,确保抽奖功能正常工作并提升用户体验。

完整的代码如下:

interface PrizeItem {
  id: number
  name: string
  icon: ResourceStr
  color: string
}

@Entry
@Component
struct 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.
  • 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.
  • 188.
  • 189.
  • 190.
  • 191.
  • 192.
  • 193.
  • 194.
  • 195.
  • 196.
  • 197.
  • 198.
  • 199.
  • 200.
  • 201.
  • 202.
  • 203.
  • 204.
  • 205.
  • 206.
  • 207.
  • 208.
  • 209.
  • 210.
  • 211.
  • 212.
  • 213.
  • 214.
  • 215.
  • 216.
  • 217.
  • 218.
  • 219.
  • 220.
  • 221.
  • 222.
  • 223.
  • 224.
  • 225.
  • 226.

核心概念解析

1. Grid组件

Grid组件是实现九宫格布局的核心,它具有以下重要属性:

  • columnsTemplate:定义网格的列模板。'1fr 1fr 1fr'表示三列等宽布局。
  • rowsTemplate:定义网格的行模板。'1fr 1fr 1fr'表示三行等高布局。
  • columnsGaprowsGap:设置列和行之间的间距。
  • aspectRatio:设置宽高比,确保网格是正方形。

2. 动画实现原理

抽奖动画的核心是通过定时器和状态更新实现的:

  1. 循环高亮:通过setTimeout定时更新currentIndex状态,实现格子的循环高亮。
  2. 动态速度:随着循环轮数的增加,逐渐增加延迟时间(this.speed += 10),实现减速效果。
  3. 结束条件:当满足两个条件时停止动画:
    • 已完成设定的总轮数(this.currentRound >= this.totalRounds
    • 当前高亮的格子是目标奖品(this.currentIndex === this.targetIndex

3. 高亮效果

格子的高亮效果是通过条件样式实现的:

.backgroundColor(index === this.currentIndex && index !== 4 ? Color.White : prize.color)
  • 1.

当格子被选中时(index === this.currentIndex),背景色变为白色,文字颜色变为奖品颜色,产生对比鲜明的高亮效果。

4. 资源清理

在组件销毁时,我们需要清除定时器以避免内存泄漏:

aboutToDisappear() {
  if (this.timer) {
    clearTimeout(this.timer)
    this.timer = 0
  }
}
  • 1.
  • 2.
  • 3.
  • 4.
  • 5.
  • 6.

进阶优化思路

完成基本功能后,可以考虑以下优化方向:

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; // 默认返回第一个奖品
}
  • 1.
  • 2.
  • 3.
  • 4.
  • 5.
  • 6.
  • 7.
  • 8.
  • 9.
  • 10.
  • 11.
  • 12.
  • 13.
  • 14.
  • 15.
  • 16.
  • 17.
  • 18.
  • 19.
  • 20.
  • 21.
  • 22.

2. 抽奖音效

添加音效可以提升用户体验:

// 播放抽奖音效
playSound(type: 'start' | 'running' | 'end') {
  // 根据不同阶段播放不同音效
}
  • 1.
  • 2.
  • 3.
  • 4.

3. 振动反馈

在抽奖开始和结束时添加振动反馈:

// 导入振动模块
import { vibrator } from '@kit.SensorServiceKit';

// 触发振动
triggerVibration() {
  vibrator.vibrate(50); // 振动50毫秒
}
  • 1.
  • 2.
  • 3.
  • 4.
  • 5.
  • 6.
  • 7.

4. 抽奖次数限制

添加抽奖次数限制和剩余次数显示:

@State remainingTimes: number = 3; // 剩余抽奖次数

startLottery() {
  if (this.isRunning || this.remainingTimes <= 0) {
    return;
  }
  
  this.remainingTimes--;
  // 其他抽奖逻辑...
}
  • 1.
  • 2.
  • 3.
  • 4.
  • 5.
  • 6.
  • 7.
  • 8.
  • 9.
  • 10.

总结

本教程从零开始,一步步实现了九宫格抽奖效果,涵盖了以下关键内容:

  1. 数据结构定义和状态管理
  2. 网格布局和循环渲染
  3. 条件样式和动画效果
  4. 定时器控制和动态速度
  5. 生命周期管理和资源清理

希望这篇 HarmonyOS Next 教程对你有所帮助,期待您的点赞、评论、收藏。

©著作权归作者所有,如需转载,请注明出处,否则将追究法律责任
已于2025-3-28 08:57:58修改
收藏
回复
举报
回复
    相关推荐
    这个用户很懒,还没有个人简介
    帖子
    视频
    声望
    粉丝
    社区精华内容