任务:使用“对象数组”、“ForEach渲染”、“Badge角标组件”、“Grid布局”等相关知识,实现生效抽奖卡案例。如图1所示:
图1 生肖抽奖卡实例图
图1(a)中有6张生肖卡可以抽奖,每抽中一张,会通过弹层显示出来(图(b)),每抽中一张,图片的右上角数量增加1,若6张卡片均抽中了(每种卡片至少有一张),就可以抽更大的奖,包括华为、苹果以及小米三种不同的手机,如图1(c)。也可实现支付宝集五福案例,功能相似即可。
参考资料:68-【阶段综合】-生肖抽卡-Badge角标组件_哔哩哔哩_bilibili
实验过程:
首先,定义了一个接口`Images`,用于描述每张生肖卡的数据结构,包含图片的URL和数量(`count`)。接着,基于这个接口准备了初始数据,即6张生肖卡,每张卡的初始数量为0。
在页面布局方面,使用了`Stack`层叠布局来实现页面的多层显示。最底层是一个`Column`布局,其中包含了一个`Grid`布局用于展示生肖卡。通过`ForEach`方法,将每张生肖卡的数据绑定到`GridItem`中,并使用`Badge`组件在每张卡的右上角显示其数量。此外,还添加了一个“立即抽卡”按钮,用于触发抽卡操作。
当点击“立即抽卡”按钮时,会触发一系列的动画效果和逻辑处理。首先,设置遮罩层的透明度、层级和缩放比例,使抽卡页面显示出来。然后,通过`Math.random()`方法生成一个随机数,表示抽中的生肖卡序号。接着,将抽中的生肖卡以放大的形式显示在抽卡页面上,并提供一个“开心收下”按钮。
点击“开心收下”按钮后,将遮罩层的透明度和层级恢复到初始状态,并将抽中的生肖卡数量加1。同时,检查是否所有生肖卡的数量都大于0,如果是,则表示集齐了所有生肖卡,可以进入中奖页面。在中奖页面中,会随机选择一个奖品(华为、苹果或小米手机),并显示相应的图片和“再来一次”按钮。点击“再来一次”按钮后,将所有生肖卡的数量重置为0,重新开始抽奖过程。
在整个实验过程中,通过合理使用`State`变量来控制页面的状态变化,以及利用`ForEach`和`Badge`组件实现了动态的数据绑定和显示效果。同时,通过`Grid`布局实现了生肖卡的规则排列,使得页面布局更加美观和清晰。
源代码:
// 1.定义接口
interface Images{
url:string
count:number
}
// 需求1:遮罩层显隐 透明度opacity 0-1 层级zInsex 1-99
// 需求2:图片收缩 缩放scale 0-1
@Entry
@Component
struct Index {
// 随机的生肖卡序号
@State randomNum:number=-1// 初始值为-1表示还没有抽卡
// 2.基于接口准备数据
@State images:Images[]=[
{url:'app.media.bg_00',count:0},
{url:'app.media.bg_01',count:0},
{url:'app.media.bg_02',count:0},
{url:'app.media.bg_03',count:0},
{url:'app.media.bg_04',count:0},
{url:'app.media.bg_05',count:0}
]
// 透明度
@State transparent:number=0 // 初始为0
//层级
@State Level:number=0 // 初始为0
// 缩放
@State zoomx:number=0
@State zoomy:number=0
// 控制中奖
@State isGet:boolean=false
// 奖池
@State arr:string[]=['pg','hw','xm']
@State price:string=''//默认没有中奖
build() {
// 层叠布局
Stack(){
// 初始化页面
Column(){
// Grid布局的基础使用:规则的行列布局中非常常见
Grid(){
ForEach(this.images,(item:Images,index:number)=>{
GridItem(){
Badge({
count:item.count,
position:BadgePosition.RightTop,
style:{
fontSize:14,
badgeSize:20,
badgeColor:Color.Red
}
}){
Image($r(item.url))
.width(100)
.height(100)
}
}
})
}
.columnsTemplate('1fr 1fr 1fr')
.rowsTemplate('1fr 1fr')
.width('100%')
.height(300)
.margin({top:100})
Button('立即抽卡')
.width(200)
.backgroundColor('#de668d')
.margin({top:50})
.onClick(()=>{
this.Level=99
this.transparent=1
this.zoomx=1
this.zoomy=1
//计算随机数
this.randomNum = Math.floor(Math.random()*6)
})
}
.zIndex(5) // 初始化层级为5
.width('100%')
.height('100%')
// 抽卡时的页面
Column({space:25}){
Text('获得生肖卡片')
.fontColor('#f5ebcf')
.fontSize(25)
.fontWeight(FontWeight.Bold)
Image($r(`app.media.img_0${this.randomNum}`))
.width(200)
// 卡片的播放
.scale({
x:this.zoomx,
y:this.zoomy
})
.animation({
duration:500
})
Button('开心收下')
.width(200)
.height(50)
.backgroundColor(Color.Transparent)// 透明色
.border({width:2,color:'#fff9e0'})
.onClick(()=>{
this.transparent=0
this.Level=0
this.zoomx=0
this.zoomy=0
// 开心收下
this.images[this.randomNum]={
url:`app.media.img_0${this.randomNum}`,
count:this.images[this.randomNum].count+1
}
// 判断卡片的数量
// 如果卡片中有一个的数量为0,那就是没集齐
let flag:boolean=true
for(let item of this.images){
if(item.count==0){
flag=false
break
}
}
this.isGet=flag
if(flag){
let randomIndex:number=Math.floor(Math.random()*3)
this.price=this.arr[randomIndex]
}
})
}
.justifyContent(FlexAlign.Center)// 居中对齐
.width('100%')
.height('100%')
.backgroundColor('#cc000000')
.zIndex(this.Level)
.opacity(this.transparent)// 透明度
// 动画
.animation({
duration:200
})
if(this.isGet){
// 中奖页面
Column(){
Text('恭喜获得手机一部')
.fontColor('#f5ebcf')
.fontSize(25)
.fontWeight(700)
Image($r(`app.media.${this.price}`))
.width(300)
Button('再来一次')
.width(200)
.height(50)
.backgroundColor(Color.Transparent)
.border({width:2,color:'#fff9e0'})
.onClick(()=>{
this.isGet=false
this.price=''
this.images=[j
{url:'app.media.img_00',count:0},
{url:'app.media.img_01',count:0},
{url:'app.media.img_02',count:0},
{url:'app.media.img_03',count:0},
{url:'app.media.img_04',count:0},
{url:'app.media.img_05',count:0}
]
})
}
.zIndex(10)
.width('100%')
.height('100%')
.justifyContent(FlexAlign.Center)
.backgroundColor('#cc000000')
}
}
}
}
运行截图: