ps:本次開發基于wepy框架
由于最近接到一個需求--抽獎活動;
翻(fan)(fan)牌打(da)亂(luan)活動(dong)抽獎(jiang)活動(dong),大(da)概需求是(shi)這樣的,九宮格卡牌,先正面展示所有(you)獎(jiang)品,然后卡牌翻(fan)(fan)轉,打(da)亂(luan)排序,點擊(ji)卡牌,然后抽獎(jiang)。
這個需求本身其(qi)實不難,主(zhu)要是(shi)分為(wei)三步(bu);
我們先在dom中渲染9個卡牌。
<view class="card-module">
<view class="card {{showClass ? 'change' : ''}}>
<view class="front card-item">{{cardItem.front}}</view>
<view class="back card-item">{{cardItem.back}}</view>
</view>
</repeat>
</view>
|
在(zai)數(shu)據中生成模(mo)擬(ni)卡牌(pai)數(shu)據
cardData: [
{
animationData: {},
front: '正面1',
back: '反面1'
},
...
...
{
animationData: {},
front: '正面9',
back: '反面9'
}
],
showClass: false,
|
在樣(yang)式(shi)(shi)中(zhong)把卡牌的基本樣(yang)式(shi)(shi)渲(xuan)染出(chu)來
.card-module{
padding: 45rpx;
display: flex;
flex-direction: row;
flex-wrap: wrap;
transform:translate3d(0,0,0);
.card{
width: 200rpx;
height: 200rpx;
line-height: 200rpx;
text-align: center;
color: #fff;
margin: 10rpx;
position:relative;
overflow:hidden;
.card-item{
position:absolute;
left:0;
top:0;
width:100%;
height:100%;
transition:all .5s ease-in-out;
transform-style:preserve-3d;
backface-visibility:hidden;
box-sizing:border-box;
}
.front{
background-color: red;
transform: rotateY(0deg);
z-index:2;
}
.back{
background-color: #009fff;
transform: rotateY(180deg);
z-index:1;
}
}
.card.change{
.front{
z-index:1;
transform: rotateY(180deg);
}
.back{
z-index:2;
transform: rotateY(0deg);
}
}
}
|
效果如下
這里有些css屬性可能需要大部(bu)補充學習一下
css3 backface-visibility 屬(shu)性
定(ding)義和用法 backface-visibility 屬性定(ding)義當(dang)元(yuan)(yuan)素不面(mian)向屏幕時是否可見。 如果在旋轉元(yuan)(yuan)素不希望看到(dao)其(qi)背(bei)面(mian)時,該屬性很有用。
CSS3 perspective 屬性(xing)
perspective 屬性(xing)定義 3D 元素(su)距(ju)視(shi)(shi)圖(tu)的距(ju)離,以像素(su)計(ji)。該屬性(xing)允許您改(gai)變(bian) 3D 元素(su)查看 3D 元素(su)的視(shi)(shi)圖(tu)。 當為(wei)元素(su)定義 perspective 屬性(xing)時,其子元素(su)會獲得透視(shi)(shi)效果,而不是元素(su)本身(shen)。
由(you)于業務上(shang)是抽獎使(shi)用(yong)的(de),所(suo)以選擇的(de)方案是:翻(fan)(fan)轉后(hou),卡牌收回到中(zhong)間的(de)卡牌中(zhong)間,然后(hou)再讓卡牌回到原來的(de)位置。 關于小(xiao)程序的(de)原生框架(jia)有(you)支持的(de)動畫接口(kou),若不(bu)了(le)(le)解的(de)請前往: developers.weixin.qq.com/miniprogram… 在對(dui)動畫有(you)基本了(le)(le)解之(zhi)后(hou),我們可以開(kai)始在翻(fan)(fan)轉的(de)基礎(chu)上(shang)加上(shang)打亂的(de)動畫了(le)(le) 微信(xin)小(xiao)程序的(de)動畫接口(kou)使(shi)用(yong)方式(shi)是在dom對(dui)象(xiang)上(shang)面加上(shang)animation對(dui)象(xiang)。 dom
<view class="card-module">
<view class="card {{showClass ? 'change' : ''}} animation="{{cardItem.animationData}}" >
<view class="front card-item">{{cardItem.front}}</view>
<view class="back card-item">{{cardItem.back}}</view>
</view>
</repeat>
</view>
|
script
allMove () {
// 110 是卡牌寬度加邊距
this.methods.shuffle.call(this, 110)
let timer = setTimeout(() => {
clearTimeout(timer)
this.methods.shuffle.call(this, 0)
this.$apply()
}, 1000)
},
// 洗牌
shuffle (translateUnit) {
let curCardData = this.cardData
curCardData.map((item, index) => {
let animation = wepy.createAnimation({
duration: 500,
timingFunction: 'ease'
})
animation.export()
switch (index) {
case 0:
animation.translate(translateUnit, translateUnit).step()
break
case 1:
animation.translate(0, translateUnit).step()
break
case 2:
animation.translate(-translateUnit, translateUnit).step()
break
case 3:
animation.translate(translateUnit, 0).step()
break
case 4:
animation.translate(0, 0).step()
break
case 5:
animation.translate(-translateUnit, 0).step()
break
case 6:
animation.translate(translateUnit, -translateUnit).step()
break
case 7:
animation.translate(0, -translateUnit).step()
break
case 8:
animation.translate(-translateUnit, -translateUnit).step()
break
}
item.animationData = animation.export()
})
this.cardData = curCardData
this.$apply()
},
|
算(suan)法(fa)后(hou)面(mian)需要優化,我(wo)們先完(wan)成功能效(xiao)(xiao)果(guo)(guo), 效(xiao)(xiao)果(guo)(guo)如下

dom代碼
<view class="card-module">
<view class="card {{showClass ? 'change' : ''}} {{curIndex === index ? 'getprize' : ''}}" @tap="itemChage({{cardItem}}, {{index}})" animation="{{cardItem.animationData}}" >
<view class="front card-item">{{cardItem.front}}</view>
<view class="back card-item">{{cardItem.back}}</view>
</view>
</repeat>
</view>
|
script代(dai)碼
data中定義一個curIndex = -1的對象
data = {
curOpen: -1,
}
methods = {
// 抽獎
itemChage (item, curIndex) {
this.cardData[curIndex].front = 'iphone x'
console.log(item, curIndex)
this.curOpen = curIndex
}
}
|
less
.card.getprize{
.front{
z-index:2;
transform: rotateY(0deg);
}
.back{
z-index:1;
transform: rotateY(180deg);
}
}
|
效果如下

現(xian)在(zai)我(wo)(wo)們就已經完成(cheng)(cheng)了基本的需求;但(dan)是在(zai)位移動畫(hua)時候代碼寫的太丑陋了。 我(wo)(wo)們來(lai)想(xiang)想(xiang)怎么優化算(suan)法; 我(wo)(wo)們現(xian)在(zai)就九宮格布局(ju),我(wo)(wo)們可以看(kan)成(cheng)(cheng)是二維布局(ju)

那我們是(shi)(shi)不是(shi)(shi)可以在(zai)卡牌(pai)中也使用二維數組布局(ju)屬性
resetData () {
const total = 9 // 總數
const lineTotal = 3 // 單行數
curCardData.map((item, index) => {
let curCardData = this.cardData
let x = index % lineTotal
let y = parseInt(index / lineTotal)
item.twoArry = {x, y}
})
}
|
在位移(yi)動(dong)畫中使用二維(wei)布局的差值進行位移(yi)
// 洗牌
shuffle (translateUnit) {
let curCardData = this.cardData
curCardData.map((item, index) => {
let animation = wepy.createAnimation({
duration: 500,
timingFunction: 'ease'
})
animation.export()
const translateUnitX = translateUnit * (1 - item.twoArry.x)
const translateUnitY = translateUnit * (1 - item.twoArry.y)
animation.translate(translateUnitX, translateUnitY).step()
item.animationData = animation.export()
})
this.cardData = curCardData
this.$apply()
},
|