接著上一個主題的內容 Lineage M 卡包動畫 - Frame animation 這次要加入抽卡的元素。
當我們打開卡包的時候,會有多張卡片在畫面上輪轉,最後慢慢停到一張卡片。
除了前一個主題做的卡包動畫外,這次新增的部分有:
卡片根據稀有度會有不一樣的背景色和氣息動畫。
enum CardLevel {
case gray
case green
case blue
case red
}
struct CardModel {
var level:CardLevel
var image:UIImage
}
通過 CardModel & CardLevel 建立一批不同稀有度的卡片。
var cards = [CardModel]()
for i in 1...36 {
var cardLevel:CardLevel = .gray
switch i % 4 {
case 0: cardLevel = .red
case 1: cardLevel = .blue
case 2: cardLevel = .green
case 3: cardLevel = .gray
default: break;
}
let card = CardModel(level: cardLevel, image: UIImage(named: "monster-\(i)")!)
cards.append(card)
}
繼續沿用上一個主題的卡包動畫,但原本打開卡片就看到卡片內容的部分拿掉了,而是變成打開後先輪播所有卡片,最後停留在最後一張卡片上。
無視性能的最簡單實現方法,建立一個 monsterScrollView 根據卡片數量來設定 width 然後將所有的卡片都放上去。
fileprivate func addMonsterView() {
monsterScrollViewMask = UIView()
monsterScrollViewMask!.frame = frame
monsterScrollViewMask?.clipsToBounds = true
addSubview(monsterScrollViewMask!)
let monsterScrollViewFrame = CGRect(x: 0, y: 0, width: frame.width * CGFloat(cards.count), height: frame.height)
monsterScrollView = UIView(frame: monsterScrollViewFrame)
monsterScrollViewMask?.addSubview(monsterScrollView!)
for i in 0..<cards.count {
let monsterFrame = CGRect(x: CGFloat(i) * frame.width, y: 0, width: frame.width, height: frame.height)
let monsterLevelView = UIImageView(frame: monsterFrame)
switch cards[i].level {
case .gray: monsterLevelView.image = UIImage(named:"img-card-bg-gray")
case .green: monsterLevelView.image = UIImage(named:"img-card-bg-green")
case .blue: monsterLevelView.image = UIImage(named:"img-card-bg-blue")
case .red: monsterLevelView.image = UIImage(named:"img-card-bg-red")
}
monsterScrollView?.addSubview(monsterLevelView)
let monsterImageView = UIImageView(frame: monsterFrame)
monsterImageView.image = cards[i].image
monsterScrollView!.contentMode = .scaleAspectFit
monsterScrollView!.addSubview(monsterImageView)
}
}
將很長的 monsterScrollView 直接 addSubView 在 SlotView 上,當打開卡包的時候將整個 monsterScrollView 向左邊移動。
這時候看起來會下面的效果,但其實我們希望卡片只會顯示在白色的框內,這方面可以通過 mask 或者 clicpsToBounds 來實現。
需要注意的是,因爲我們所有的 View 都是放在 SlotView 上的,
如果單純直接對 SlotView 設定 clickpsToBounds = true 卡片外的效果就會被切掉,比如卡包背後超出卡片的氣息動畫。
fileprivate func startScrollingSlot() {
if monsterScrollView == nil { return }
if isScrolling {
return
} else {
isScrolling = true
}
shineOnce()
// show border when scrolling
layer.borderColor = UIColor.white.cgColor
layer.borderWidth = 1
// reset position
let originalFrame = monsterScrollView!.frame
monsterScrollView!.frame = CGRect(x: 0, y: 0, width: originalFrame.width, height: originalFrame.height)
let newFrame = CGRect(x: -self.frame.width * CGFloat(cards.count - 1),
y: 0,
width: self.frame.width,
height: self.frame.height)
UIView.animate(withDuration: 3, delay: 0, options: .curveEaseOut, animations: {
self.monsterScrollView!.frame = newFrame
}, completion: { finished in
self.isScrolling = false
self.shineOnce()
self.showCardAura()
// hide border when scroll ended
self.layer.borderWidth = 0.0
})
}
打開卡包的一瞬間以及卡片移動停止的時候,畫面都會閃現一下,這裡一樣是通過一張光亮的圖片,通過控制 Alpha 來實現。
fileprivate func addShineView() {
let shineMask = UIView(frame: frame)
shineMask.clipsToBounds = true
addSubview(shineMask)
shineView = UIImageView(frame: CGRect(x: 0, y: 0, width: frame.width * 1.3, height: frame.height * 1.3))
shineView?.center = center
shineView!.image = UIImage(named:"img-card-shime")!
shineView?.contentMode = .scaleAspectFill
shineView!.alpha = 0.0
shineMask.addSubview(shineView!)
}
優化
這次因為只有一個長條圖在畫面上移動,所以即使是在最近效能不好的模擬器上移動都不會有卡頓的感覺,但實際上 SlotMachine 常常是要同時有多個輪播圖的效果,那麼上面的例子就不適合了。
更好的作法應該是類似 UITableView/UICollectionView 的方式,留個 preload 空間,剩下的圖都不留在畫面上,比如這次的例子,畫面上其實留5張圖片就夠用了。
shineView = UIImageView(frame: CGRect(x: 0, y: 0, width: frame.width * 1.3, height: frame.height * 1.3))
我一直在想為什麼要*1.3 不是會被擋掉嗎 下載soucecode看到原圖就懂了.... 高招高招
嘿嘿嘿嘿....偷懶方法......每天要抽時間寫鐵人蠻辛苦的 QQ