ArkTs 实现抽卡效果

771 阅读4分钟

在移动应用开发中,抽卡机制作为一种常见的用户互动方式,广泛应用于各类游戏中,以提升用户体验和增强游戏的趣味性。ArkTS(Ark TypeScript)作为OpenHarmony系统下的主要编程语言,继承了TypeScript的大部分语法特性,并针对移动开发场景进行了优化,旨在实现更高效的运行时性能和更低的资源消耗。本文将详细介绍如何使用ArkTS来实现一个基础的抽卡效果,通过结合界面布局、状态管理、及用户交互,来展现ArkTS在开发过程中的强大功能和灵活性。

接口定义

interface Icart {
  bgcart: ResourceStr //背景图
  frontcart: ResourceStr //卡牌
  count: number //用于计数
}

接口定义了图片的数据结构,便于图片的处理以及卡牌计数

实现思路

  • 大布局 立即抽卡 开心收下
  • 点击立即抽卡 显示中卡页面
  • 判断是否获取全部卡片
  • 首先我们要定义一个数字类型的数据,判断 如果是初始页面数字为0 ,中奖页面数字为1 ,集齐所有卡牌数字为2 来切换页面

页面结构和布局

  • 实现抽卡的页面主要是用 stack()层叠布局把控全局,Flex()弹性布局负责卡片的布局
//静态布局
Column() {
  Stack() {
    Column({ space: 50 }) {
      Flex({ wrap: FlexWrap.Wrap }) {
        ForEach(this.cart, (item: Icart, index,) => {
          FlowItem() {
            //Badge 用来计数 它可以计算获得卡牌相同的个数
            Badge({ count: item.count, style: {} }) {
              Image(item.count > 0 ? item.frontcart : item.bgcart)
                .width(110)
                .margin({ bottom: 10 })
            }
          }
          .width('33.33%')
        })
      }

      Button('立即抽卡')
        .width(200)
        .backgroundColor(Color.Pink)
        .onClick(() => {
          this.curren = Math.floor(Math.random() * this.cart.length)
          let item = this.cart[this.curren]
          item.count++ //计数
          this.cart.splice(this.curren, 1, item)
          this.num = 1
        })
    }

    //点击抽卡 num赋值给1
    //遮盖层
    if (this.num == 1) {
      Column({ space: 44 }) {
        Text('获得卡牌')
          .fontSize(30)
        Image(this.cart[this.curren].frontcart)
          .width('65%')
        Button('开心收下')//统计是否获得全部
          .backgroundColor(Color.Pink)
          .width(200)
          .onClick(() => {
            let count = 0
            for (let index = 0; index < this.cart.length; index++) {
              const element = this.cart[index]
              if (element.count > 0) {
                count++
              }
              if (count == this.cart.length) {
                this.num = 2
              } else {
                this.num = 0
              }
            }
          })

      }
      .width('100%')
      .height('100%')
      .backgroundColor('rgba( 43, 43, 43 ,0.4)')
    }
    if (this.num == 2) {
      Column() {
        Text('恭喜你获得全部卡牌')
          .fontColor(Color.Red)
          .fontSize(35)
          .margin({ bottom: 100 })
        Button('确认')
          .backgroundColor(Color.Pink)
          .width(200)
          .translate({ y: 185 })
          .onClick(() => {
            this.num = 0
            this.cart = [{ bgcart: $r('app.media.10007'), frontcart: $r('app.media.10001'), count: 0 },              { bgcart: $r('app.media.10007'), frontcart: $r('app.media.10002'), count: 0 },              { bgcart: $r('app.media.10007'), frontcart: $r('app.media.10003'), count: 0 },              { bgcart: $r('app.media.10007'), frontcart: $r('app.media.10004'), count: 0 },              { bgcart: $r('app.media.10007'), frontcart: $r('app.media.10005'), count: 0 },              { bgcart: $r('app.media.10007'), frontcart: $r('app.media.10006'), count: 0 },]
          })
      }
      .justifyContent(FlexAlign.Center)
      .width('100%')
      .height('100%')
      .backgroundColor('rgba( 43, 43, 43 ,0.9)')
    }
  }
  .width('100%')
  .height('100%')
}
  • Text 用于显示页面标题
  • Button 组件添加了点击事件处理,触发抽卡效果并更新相关状态变量

卡片计数

          FlowItem() {
            //Badge 用来计数 它可以计算获得卡牌相同的个数
            Badge({ count: item.count, style: {} }) {
              Image(item.count > 0 ? item.frontcart : item.bgcart)
                .width(110)
                .margin({ bottom: 10 })
            }
          }
          .width('33.33%')
        })

点击立即抽卡

  Button('立即抽卡')
    .width(200)
    .backgroundColor(Color.Pink)
    .onClick(() => {
      this.curren = Math.floor(Math.random() * this.cart.length) //随机获得一个0-5数据
      let item = this.cart[this.curren]
      item.count++ //计数
      this.cart.splice(this.curren, 1, item)
      this.num = 1
      //延时器  卡片动画效果
      setTimeout(() => {
        this.scalecart = 1
      }, 100)
    })
}

获得卡片

//点击抽卡 num赋值给1
//遮盖层
if (this.num == 1) {
  Column({ space: 44 }) {
    Text('获得卡牌')
      .fontSize(30)
    Image(this.cart[this.curren].frontcart)
      .width('65%')
      .scale({
        x: this.scalecart, y: this.scalecart
      })
      .animation({
        duration: 500
      })
    Button('开心收下')//统计是否获得全部
      .backgroundColor(Color.Pink)
      .width(200)
      .onClick(() => {
        let count = 0
        for (let index = 0; index < this.cart.length; index++) {
          const element = this.cart[index]
          if (element.count > 0) {
            count++
          }
          if (count == this.cart.length) {
            this.num = 2
          } else {
            this.num = 0
          }
        }
        this.scalecart = 0
      })

  }
  .width('100%')
  .height('100%')
  .backgroundColor('rgba( 43, 43, 43 ,0.4)')
}

集齐所有卡牌,数据清理

if (this.num == 2) {
    Column() {
      Text('恭喜你获得全部卡牌')
        .fontColor(Color.Red)
        .fontSize(35)
        .margin({ bottom: 100 })
      Button('确认')
        .backgroundColor(Color.Pink)
        .width(200)
        .translate({ y: 185 })
        .onClick(() => {
          this.num = 0
          //数据重置
          this.cart = [{ bgcart: $r('app.media.10007'), frontcart: $r('app.media.10001'), count: 0 },
            { bgcart: $r('app.media.10007'), frontcart: $r('app.media.10002'), count: 0 },
            { bgcart: $r('app.media.10007'), frontcart: $r('app.media.10003'), count: 0 },
            { bgcart: $r('app.media.10007'), frontcart: $r('app.media.10004'), count: 0 },
            { bgcart: $r('app.media.10007'), frontcart: $r('app.media.10005'), count: 0 },
            { bgcart: $r('app.media.10007'), frontcart: $r('app.media.10006'), count: 0 },]
        })
    }
    .justifyContent(FlexAlign.Center)
    .width('100%')
    .height('100%')
    .backgroundColor('rgba( 43, 43, 43 ,0.9)')
  }
}
.width('100%')
.height('100%')

整体代码及运行

interface Icart {
  bgcart: ResourceStr //背景图
  frontcart: ResourceStr //卡牌
  count: number //用于计数
}


@Entry
@Component
struct Index {
  //获取卡片的数据
  @State
  cart: Icart[] = [{ bgcart: $r('app.media.10007'), frontcart: $r('app.media.10001'), count: 0 },
    { bgcart: $r('app.media.10007'), frontcart: $r('app.media.10002'), count: 0 },
    { bgcart: $r('app.media.10007'), frontcart: $r('app.media.10003'), count: 0 },
    { bgcart: $r('app.media.10007'), frontcart: $r('app.media.10004'), count: 0 },
    { bgcart: $r('app.media.10007'), frontcart: $r('app.media.10005'), count: 0 },
    { bgcart: $r('app.media.10007'), frontcart: $r('app.media.10006'), count: 0 },]
  @State curren: number = -1
  @State num: number = 0
  @State scalecart: number = 0

  build() {
    //静态布局
    Column() {
      Stack() {
        Column({ space: 50 }) {
          Flex({ wrap: FlexWrap.Wrap }) {
            ForEach(this.cart, (item: Icart, index,) => {
              FlowItem() {
                //Badge 用来计数 它可以计算获得卡牌相同的个数
                Badge({ count: item.count, style: {} }) {
                  Image(item.count > 0 ? item.frontcart : item.bgcart)
                    .width(110)
                    .margin({ bottom: 10 })
                }
              }
              .width('33.33%')
            })
          }

          Button('立即抽卡')
            .width(200)
            .backgroundColor(Color.Pink)
            .onClick(() => {
              this.curren = Math.floor(Math.random() * this.cart.length)
              let item = this.cart[this.curren]
              item.count++ //计数
              this.cart.splice(this.curren, 1, item)
              this.num = 1
              //延时器
              setTimeout(() => {
                this.scalecart = 1
              }, 100)
            })
        }

        //点击抽卡 num赋值给1
        //遮盖层
        if (this.num == 1) {
          Column({ space: 44 }) {
            Text('获得卡牌')
              .fontSize(30)
            Image(this.cart[this.curren].frontcart)
              .width('65%')
              .scale({
                x: this.scalecart, y: this.scalecart
              })
              .animation({
                duration: 500
              })
            Button('开心收下')//统计是否获得全部
              .backgroundColor(Color.Pink)
              .width(200)
              .onClick(() => {
                let count = 0
                for (let index = 0; index < this.cart.length; index++) {
                  const element = this.cart[index]
                  if (element.count > 0) {
                    count++
                  }
                  if (count == this.cart.length) {
                    this.num = 2
                  } else {
                    this.num = 0
                  }
                }
                this.scalecart = 0
              })

          }
          .width('100%')
          .height('100%')
          .backgroundColor('rgba( 43, 43, 43 ,0.4)')
        }
        if (this.num == 2) {
          Column() {
            Text('恭喜你获得全部卡牌')
              .fontColor(Color.Red)
              .fontSize(35)
              .margin({ bottom: 100 })
            Button('确认')
              .backgroundColor(Color.Pink)
              .width(200)
              .translate({ y: 185 })
              .onClick(() => {
                 //返回初始页面
                this.num = 0
                //获得全部卡牌时数据清零
                this.cart = [{ bgcart: $r('app.media.10007'), frontcart: $r('app.media.10001'), count: 0 },                  { bgcart: $r('app.media.10007'), frontcart: $r('app.media.10002'), count: 0 },                  { bgcart: $r('app.media.10007'), frontcart: $r('app.media.10003'), count: 0 },                  { bgcart: $r('app.media.10007'), frontcart: $r('app.media.10004'), count: 0 },                  { bgcart: $r('app.media.10007'), frontcart: $r('app.media.10005'), count: 0 },                  { bgcart: $r('app.media.10007'), frontcart: $r('app.media.10006'), count: 0 },]
              })
          }
          .justifyContent(FlexAlign.Center)
          .width('100%')
          .height('100%')
          .backgroundColor('rgba( 43, 43, 43 ,0.9)')
        }
      }
      .width('100%')
      .height('100%')
    }
  }
}

PixPin_2024-08-18_21-28-07.gif