鸿蒙案例---生肖抽卡

发布于:2025-04-12 ⋅ 阅读:(38) ⋅ 点赞:(0)

案例源码:

Zodiac_cards: 鸿蒙生肖抽奖卡片

效果演示

初始布局

1. Badge 角标组件

此处为语雀内容卡片,点击链接查看:https://www.yuque.com/kevin-nzthp/lvl039/rccg0o4pkp3v6nua

2. Grid 布局

// 定义接口
interface ImageCount {
  url: ResourceStr,
  count: number
}

@Entry
  @Component
  struct Index {
    @State images: ImageCount[] = [
      { url: '/images/bg_00.png', count: 0 },
      { url: '/images/bg_01.png', count: 1 },
      { url: '/images/bg_02.png', count: 2 },
      { url: '/images/bg_03.png', count: 3 },
      { url: '/images/bg_04.png', count: 4 },
      { url: '/images/bg_05.png', count: 5 },
    ]

    // 控制遮罩的显隐
    @State maskOpacity: number = 0 // 透明度
    @State maskIndex: number = -1; // 显示层级

    // 控制图片的缩放
    @State maskImgX: number = 0 // 水平缩放比
    @State maskImgY: number = 0 // 垂直缩放比

    build() {
      Stack() {
        Column() {
          Grid() {
            ForEach(this.images, (item: ImageCount) => {
              GridItem() {
                Badge({
                  count: item.count,
                  position: BadgePosition.RightTop,
                  style: {
                    badgeSize: this.maskImgX,
                    fontSize: this.maskImgY
                  }
                }) {
                  Image(item.url)
                    .width(80)
                }
              }
            })
          }
          .rowsTemplate('1fr 1fr')
            .columnsTemplate('1fr 1fr 1fr')
            .width('100%')
            .height(300)
            .margin({ top: 100 })


          Button('立即抽卡')
            .width(200)
            .backgroundColor('#ed5b8c')
            .margin({ top: 50 })
            .onClick(()=>{
              // 点击时,修改遮罩参数,让遮罩显示
              this.maskOpacity = 1
              this.maskIndex = 99
              // 图片需要缩放
              this.maskImgX = 1
              this.maskImgY = 1
            })

        }
        .width('100%')
          .height('100%')
          .backgroundColor(Color.Pink)

        // 抽卡遮盖层
        Column({space: 30}) {
          Text('获得生肖卡')
            .fontColor('#f5ebcf')
            .fontSize(25)
            .fontWeight(FontWeight.Bold)
          Image('/images/img_00.png')
            .width(200)
            //控制元素的缩放
            .scale({
              x: this.maskImgX,
              y: this.maskImgY
            })

          Button('开心收下')
            .width(200)
            .height(50)
            .backgroundColor(Color.Transparent)
            .border({ width: 2 ,color:'#fff9e0'})
            .onClick(()=>{
              // 控制弹层显隐
              this.maskOpacity = 0
              this.maskIndex = -1
              // 重置缩放比为0,便于下一次进行缩放
              this.maskImgX = 0
              this.maskImgY = 0
            })
        }
        .justifyContent(FlexAlign.Center)
          .width('100%')
          .height('100%')
          .backgroundColor('#cc000000')
          // 设置透明度
          .opacity(this.maskOpacity)
          .zIndex(this.maskIndex)
          // 动画 animation 当我们元素有状态的改变,可以添加animation做动画
          .animation({
            duration: 500
          })
      }
    }
  }

抽卡遮罩层

静态页面

点击立即抽卡按钮后,会进入遮罩层,显示抽取的卡片,此时抽卡的页面转换为背景图,使用层叠布局

// 定义图片接口
interface ImageCount {
  url: ResourceStr,
  count: number
}

@Entry
  @Component
  struct Index {
    // 定义图片渲染数组
    @State images: ImageCount[] = [
      { url: '/images/bg_00.png', count: 0 },
      { url: '/images/bg_01.png', count: 1 },
      { url: '/images/bg_02.png', count: 2 },
      { url: '/images/bg_03.png', count: 3 },
      { url: '/images/bg_04.png', count: 4 },
      { url: '/images/bg_05.png', count: 5 },
    ]

    build() {
      Stack(){
        Column() {
          Grid() {
            ForEach(this.images, (item: ImageCount) => {
              GridItem() {
                Badge({
                  count: item.count,
                  position: BadgePosition.RightTop,
                  style: {
                    fontSize: 12,
                    badgeSize: 16
                  }
                }) {
                  Image(item.url)
                    .width(80)
                }
              }
            })
          }
          .rowsTemplate('1fr 1fr')
            .columnsTemplate('1fr 1fr 1fr')
            .height(300)
            .margin({top: 50, bottom: 50})
          // .backgroundColor(Color.Pink)

          Button('立即抽卡')
            .width(200)
            .backgroundColor('#ED5B8C')
        }

        Column({space: 30}){
          Text('获得生肖卡')
            .fontColor('#F3EAD3')
            .fontWeight(700)
            .fontSize(24)

          Image('/images/img_00.png')
            .width(200)

          Button('开心收下')
            .width(200)
            .border({
              width: 2,
              color:'#9F9C90',
            })
            .backgroundColor(Color.Transparent)
        }
        .backgroundColor('#cc000000')
          .width('100%')
          .height('100%')
          .justifyContent(FlexAlign.Center)

      }
    }
  }

抽卡遮罩层- 显隐效果控制

添加状态变量控制遮罩层 Z 轴 和 不透明度的数值.

当点击 “立即抽卡”按钮时,显示遮罩层。(此时不能隐藏)

当点击 遮罩层“开心收下”按钮时,隐藏遮罩层。

添加动画

添加遮罩层图片的缩放

效果

// 定义图片接口
interface ImageCount {
  url: ResourceStr,
  count: number
}

@Entry
  @Component
  struct Index {
    // 定义图片渲染数组
    @State images: ImageCount[] = [
      { url: '/images/bg_00.png', count: 0 },
      { url: '/images/bg_01.png', count: 1 },
      { url: '/images/bg_02.png', count: 2 },
      { url: '/images/bg_03.png', count: 3 },
      { url: '/images/bg_04.png', count: 4 },
      { url: '/images/bg_05.png', count: 5 },
    ]
    // 控制遮罩的显隐
    @State maskOpacity: number = 0 // 透明度
    @State maskIndex: number = -1; // 显示层级
    // 控制遮罩层图片的缩放
    @State maskImgScaleX: number = 0 // 水平缩放比
    @State maskImgScaleY: number = 0 // 垂直缩放比

    // 获取图片

    build() {
      Stack() {
        // 抽卡层
        Column() {
          Grid() {
            ForEach(this.images, (item: ImageCount) => {
              GridItem() {
                Badge({
                  count: item.count,
                  position: BadgePosition.RightTop,
                  style: {
                    fontSize: 12,
                    badgeSize: 16
                  }
                }) {
                  Image(item.url)
                    .width(80)
                }
              }
            })
          }
          .rowsTemplate('1fr 1fr')
            .columnsTemplate('1fr 1fr 1fr')
            .height(300)
            .margin({ top: 50, bottom: 50 })

          // .backgroundColor(Color.Pink)

          Button('立即抽卡')
            .width(200)
            .backgroundColor('#ED5B8C')
            .onClick(() => {
              // 点击时,修改遮罩参数,让遮罩显示
              this.maskOpacity = 1
              this.maskIndex = 99
              // 点击时修改遮罩层图片的缩放比
              this.maskImgScaleX = 1
              this.maskImgScaleY = 1
            })
        }

        // 遮罩层
        Column({ space: 30 }) {
          Text('获得生肖卡')
            .fontColor('#F3EAD3')
            .fontWeight(700)
            .fontSize(24)

          Image('/images/img_00.png')
            .width(200)
            .scale({
              //控制图片的缩放
              x: this.maskImgScaleX,
              y: this.maskImgScaleY
            })
            .animation({
              // 动画
              duration: 500
            })

          Button('开心收下')
            .width(200)
            .border({
              width: 2,
              color: '#9F9C90',
            })
            .backgroundColor(Color.Transparent)
            .onClick(() => {
              // 点击时,修改遮罩参数,让遮罩隐藏
              this.maskOpacity = 0
              this.maskIndex = -1
              //   // 点击时修改遮罩层图片的缩放比为1:1
              this.maskImgScaleX = 0
              this.maskImgScaleY = 0
            })
        }
        .zIndex(this.maskIndex)
          .opacity(this.maskOpacity)
          .backgroundColor('#cc000000')
          .width('100%')
          .height('100%')
          .justifyContent(FlexAlign.Center)

      }
    }
  }

随机卡片

效果演示:

要获得 0-5 的整数索引,随机抽取卡片

此时可以获取随机卡片,接下来要将抽到的随机卡片显示在主页面并右上角角标显示。

// 定义图片接口
interface ImageCount {
  url: ResourceStr,
  count: number
}

@Entry
  @Component
  struct Index {
    // 定义图片渲染数组
    @State images: ImageCount[] = [
      { url: '/images/bg_00.png', count: 0 },
      { url: '/images/bg_01.png', count: 0 },
      { url: '/images/bg_02.png', count: 0 },
      { url: '/images/bg_03.png', count: 0 },
      { url: '/images/bg_04.png', count: 0 },
      { url: '/images/bg_05.png', count: 0 },
    ]
    // 控制遮罩的显隐
    @State maskOpacity: number = 0 // 透明度
    @State maskIndex: number = -1; // 显示层级
    // 控制遮罩层图片的缩放
    @State maskImgScaleX: number = 0 // 水平缩放比
    @State maskImgScaleY: number = 0 // 垂直缩放比
    // 获取遮罩层选择的图片Index
    @State maskImgIndex: number = 0

    build() {
      Stack() {
        // 抽卡层
        Column() {
          Grid() {
            ForEach(this.images, (item: ImageCount) => {
              GridItem() {
                Badge({
                  count: item.count,
                  position: BadgePosition.RightTop,
                  style: {
                    fontSize: 12,
                    badgeSize: 16
                  }
                }) {
                  Image(item.url)
                    .width(80)
                }
              }
            })
          }
          .rowsTemplate('1fr 1fr')
            .columnsTemplate('1fr 1fr 1fr')
            .height(300)
            .margin({ top: 50, bottom: 50 })

          // .backgroundColor(Color.Pink)

          Button('立即抽卡')
            .width(200)
            .backgroundColor('#ED5B8C')
            .onClick(() => {
              // 点击时,修改遮罩参数,让遮罩显示
              this.maskOpacity = 1
              this.maskIndex = 99
              // 点击时修改遮罩层图片的缩放比
              this.maskImgScaleX = 1
              this.maskImgScaleY = 1
              //   // 随机获取图片的Index
              this.maskImgIndex = Math.floor(Math.random() * 6)
            })
        }

        // 遮罩层
        Column({ space: 30 }) {
          Text('获得生肖卡')
            .fontColor('#F3EAD3')
            .fontWeight(700)
            .fontSize(24)

          Image(`/images/img_0${this.maskImgIndex}.png`)
            .width(200)
            .scale({
              //控制图片的缩放
              x: this.maskImgScaleX,
              y: this.maskImgScaleY
            })
            .animation({
              // 动画
              duration: 500
            })

          Button('开心收下')
            .width(200)
            .border({
              width: 2,
              color: '#9F9C90',
            })
            .backgroundColor(Color.Transparent)
            .onClick(() => {
              // 点击时,修改遮罩参数,让遮罩隐藏
              this.maskOpacity = 0
              this.maskIndex = -1
              //   // 点击时修改遮罩层图片的缩放比为1:1
              this.maskImgScaleX = 0
              this.maskImgScaleY = 0
              //   开心收下
              this.images[this.maskImgIndex] = {
                url: `/images/img_0${this.maskImgIndex}.png`,
                count: this.images[this.maskImgIndex].count + 1
              }
            })
        }
        .zIndex(this.maskIndex)
          .opacity(this.maskOpacity)
          .backgroundColor('#cc000000')
          .width('100%')
          .height('100%')
          .justifyContent(FlexAlign.Center)

    }
  }
}

抽大奖遮罩层

静态页面

// 定义图片接口
interface ImageCount {
  url: ResourceStr,
  count: number
}

@Entry
@Component
struct Index {
  // 定义图片渲染数组
  @State images: ImageCount[] = [
    { url: '/images/bg_00.png', count: 0 },
    { url: '/images/bg_01.png', count: 0 },
    { url: '/images/bg_02.png', count: 0 },
    { url: '/images/bg_03.png', count: 0 },
    { url: '/images/bg_04.png', count: 0 },
    { url: '/images/bg_05.png', count: 0 },
  ]
  // 控制遮罩的显隐
  @State maskOpacity: number = 0 // 透明度
  @State maskIndex: number = -1; // 显示层级
  // 控制遮罩层图片的缩放
  @State maskImgScaleX: number = 0 // 水平缩放比
  @State maskImgScaleY: number = 0 // 垂直缩放比
  // 获取遮罩层选择的图片Index
  @State maskImgIndex: number = 0

  build() {
    Stack() {
      // 抽卡层
      Column() {
        Grid() {
          ForEach(this.images, (item: ImageCount) => {
            GridItem() {
              Badge({
                count: item.count,
                position: BadgePosition.RightTop,
                style: {
                  fontSize: 12,
                  badgeSize: 16
                }
              }) {
                Image(item.url)
                  .width(80)
              }
            }
          })
        }
        .rowsTemplate('1fr 1fr')
        .columnsTemplate('1fr 1fr 1fr')
        .height(300)
        .margin({ top: 50, bottom: 50 })

        // .backgroundColor(Color.Pink)

        Button('立即抽卡')
          .width(200)
          .backgroundColor('#ED5B8C')
          .onClick(() => {
            // 点击时,修改遮罩参数,让遮罩显示
            this.maskOpacity = 1
            this.maskIndex = 99
            // 点击时修改遮罩层图片的缩放比
            this.maskImgScaleX = 1
            this.maskImgScaleY = 1
            //   // 随机获取图片的Index
            this.maskImgIndex = Math.floor(Math.random() * 6)
          })
      }

      // 遮罩层
      Column({ space: 30 }) {
        Text('获得生肖卡')
          .fontColor('#F3EAD3')
          .fontWeight(700)
          .fontSize(24)

        Image(`/images/img_0${this.maskImgIndex}.png`)
          .width(200)
          .scale({
            //控制图片的缩放
            x: this.maskImgScaleX,
            y: this.maskImgScaleY
          })
          .animation({
            // 动画
            duration: 500
          })

        Button('开心收下')
          .width(200)
          .border({
            width: 2,
            color: '#9F9C90',
          })
          .backgroundColor(Color.Transparent)
          .onClick(() => {
            // 点击时,修改遮罩参数,让遮罩隐藏
            this.maskOpacity = 0
            this.maskIndex = -1
            //   // 点击时修改遮罩层图片的缩放比为1:1
            this.maskImgScaleX = 0
            this.maskImgScaleY = 0
          //   开心收下
            this.images[this.maskImgIndex] = {
              url: `/images/img_0${this.maskImgIndex}.png`,
              count: this.images[this.maskImgIndex].count + 1
            }
          })
      }
      .zIndex(this.maskIndex)
      .opacity(this.maskOpacity)
      .backgroundColor('#cc000000')
      .width('100%')
      .height('100%')
      .justifyContent(FlexAlign.Center)

      // 抽大奖遮罩层
      Column({space: 30}) {
        Text('恭喜获得手机一部')
          .fontColor('#E4DDC7')
          .fontWeight(700)
          .fontSize(25)
        Image('/images/hw.png')
          .width(300)
        Button('再来一次')
          .width(200)
          .height(50)
          .border({
            width: 2,
            color: '#E4DDC7'
          })
          .backgroundColor(Color.Transparent)
      }
      .justifyContent(FlexAlign.Center)
      .width('100%')
      .height('100%')
      .backgroundColor('#cc000000')

    }
  }
}

抽大奖遮罩层的显隐

前提:

六张卡片集齐,显示 --- 中大奖页面

默认为 false,不显示此抽大奖遮罩层

判断数组项的count, 是否都大于0, 只能有一个等于0,就意味着没及其

最终效果演示

随机奖品 & 再来一次

奖品随机抽 -》准备一个奖品数组, Math

再来一次 -》重置数据

奖品随机抽

准备奖品数组,默认抽中的奖品为空

准备随机数

在“”开心收下“”按钮下,判断是否中奖,如果中奖了,准备抽奖。

效果:

再来一次

将数据重置

效果演示:

完整代码:

import { trustedAppService } from '@kit.DeviceSecurityKit';

// 定义图片接口
interface ImageCount {
  url: ResourceStr,
  count: number
}

@Entry
  @Component
  struct Index {
    // 定义图片渲染数组
    @State images: ImageCount[] = [
      { url: '/images/bg_00.png', count: 0 },
      { url: '/images/bg_01.png', count: 0 },
      { url: '/images/bg_02.png', count: 0 },
      { url: '/images/bg_03.png', count: 0 },
      { url: '/images/bg_04.png', count: 0 },
      { url: '/images/bg_05.png', count: 0 },
    ]
    // 奖品池
    @State prizePool: string[] = [
      '/images/pg.png',
      '/images/hw.png',
      '/images/xm.png'
    ]
    //抽中的奖品
    @State prize: string = '' // 默认没中奖
    // 控制遮罩的显隐
    @State maskOpacity: number = 0 // 透明度
    @State maskIndex: number = -1; // 显示层级
    // 控制遮罩层图片的缩放
    @State maskImgScaleX: number = 0 // 水平缩放比
    @State maskImgScaleY: number = 0 // 垂直缩放比
    // 获取遮罩层选择的图片Index
    @State maskImgIndex: number = 0
    // 控制中大奖的显隐
    @State isGet: boolean = false // 中大奖显隐

    build() {
      Stack() {
        // 抽卡层
        Column() {
          Grid() {
            ForEach(this.images, (item: ImageCount) => {
              GridItem() {
                Badge({
                  count: item.count,
                  position: BadgePosition.RightTop,
                  style: {
                    fontSize: 12,
                    badgeSize: 16
                  }
                }) {
                  Image(item.url)
                    .width(80)
                }
              }
            })
          }
          .rowsTemplate('1fr 1fr')
            .columnsTemplate('1fr 1fr 1fr')
            .height(300)
            .margin({ top: 50, bottom: 50 })

          // .backgroundColor(Color.Pink)

          Button('立即抽卡')
            .width(200)
            .backgroundColor('#ED5B8C')
            .onClick(() => {
              // 点击时,修改遮罩参数,让遮罩显示
              this.maskOpacity = 1
              this.maskIndex = 99
              // 点击时修改遮罩层图片的缩放比
              this.maskImgScaleX = 1
              this.maskImgScaleY = 1
              //   // 随机获取图片的Index
              this.maskImgIndex = Math.floor(Math.random() * 6)
            })
        }

        // 遮罩层
        Column({ space: 30 }) {
          Text('获得生肖卡')
            .fontColor('#F3EAD3')
            .fontWeight(700)
            .fontSize(24)

          Image(`/images/img_0${this.maskImgIndex}.png`)
            .width(200)
            .scale({
              //控制图片的缩放
              x: this.maskImgScaleX,
              y: this.maskImgScaleY
            })
            .animation({
              // 动画
              duration: 500
            })

          Button('开心收下')
            .width(200)
            .border({
              width: 2,
              color: '#9F9C90',
            })
            .backgroundColor(Color.Transparent)
            .onClick(() => {
              // 点击时,修改遮罩参数,让遮罩隐藏
              this.maskOpacity = 0
              this.maskIndex = -1
              //   // 点击时修改遮罩层图片的缩放比为1:1
              this.maskImgScaleX = 0
              this.maskImgScaleY = 0
              //   开心收下
              this.images[this.maskImgIndex] = {
                url: `/images/img_0${this.maskImgIndex}.png`,
              count: this.images[this.maskImgIndex].count + 1
            }
            // 每次收完,要进行简单检索,判断是否集齐
            // 需求:判断数组项的count, 是否都大于0, 只能有一个等于0,就意味着没及其
            let flag: boolean = true // 假设集齐
            // 验证是否集齐
            for (let item of this.images) {
              if (item.count === 0) {
                flag = false // 没集齐
                break; // 只要没集齐,便可退出循环
              }
            }
            this.isGet = flag
            if (flag) {
              let randIndex: number = Math.floor(Math.random() * 3)
              this.prize = this.prizePool[randIndex]
            }
          })
      }
      .zIndex(this.maskIndex)
      .opacity(this.maskOpacity)
      .backgroundColor('#cc000000')
      .width('100%')
      .height('100%')
      .justifyContent(FlexAlign.Center)

      // 抽大奖遮罩层
      if (this.isGet) {
        Column({ space: 30 }) {
          Text('恭喜获得手机一部')
            .fontColor('#E4DDC7')
            .fontWeight(700)
            .fontSize(25)
          Image(this.prize)
            .width(300)
          Button('再来一次')
            .width(200)
            .height(50)
            .border({
              width: 2,
              color: '#E4DDC7'
            })
            .backgroundColor(Color.Transparent)
            .onClick(() => {
              this.isGet = false
              this.prize = ''
              this.images = [
                { url: '/images/bg_00.png', count: 0 },
                { url: '/images/bg_01.png', count: 0 },
                { url: '/images/bg_02.png', count: 0 },
                { url: '/images/bg_03.png', count: 0 },
                { url: '/images/bg_04.png', count: 0 },
                { url: '/images/bg_05.png', count: 0 },
              ]
            })
        }
        .justifyContent(FlexAlign.Center)
        .width('100%')
        .height('100%')
        .backgroundColor('#cc000000')
      }
    }
  }
}