iT邦幫忙

2018 iT 邦幫忙鐵人賽
DAY 17
1

https://ithelp.ithome.com.tw/upload/images/20180105/20107329u7d9vEIfPd.png
快過年了,這次做一個一堆錢飛向存錢筒的動畫效果。

BankCoins

BankCoins

畫面正上方放著一個存錢筒,點下「Show me the money」以後,會有300個金幣、銀幣從畫面底部飛向存錢筒。

錢幣飛行的過程會逐漸變小,到達存錢筒的位置時會消失。


晃動存錢筒

BankCoins

讓 bankView 根據 Z 軸進行來回三次的 rotation 的動畫。

fileprivate func shakeBank() {
    let shake = CABasicAnimation(keyPath: "transform.rotation.z")
    shake.fromValue = -0.2
    shake.toValue = 0.2
    shake.duration = 0.1
    shake.autoreverses = true
    shake.repeatCount = 3
    
    bankView.layer.add(shake, forKey: "bankShakeAnimation")
}

初始化錢幣

錢幣的動畫是這樣的,一開始在畫面底部生成 300個金幣、銀幣,接著為每一個錢幣延遲做一個動畫,飛向存錢筒。

通過 Dispatch.main.asyncAfter 方法延遲每一個初始化的金幣及其動畫。

根據順序為錢幣分配 tag 和錢幣圖案的分配(金幣或銀幣)

這時候錢幣的 center 位置,是存錢筒的 center 偏上 20 個單位。

fileprivate func startCoinanimation() {
    fireButton.isHidden = true
    lastFinishedCoinNumber = 0
    for i in 0...totalCoinCount {
        DispatchQueue.main.asyncAfter(deadline: .now() + Double(i) * 0.01, execute: {
            self.initCoinWith(number: i)
        })
    }
}

fileprivate func initCoinWith(number:Int) {
    let coinImageName = "icon_coin_\(number % 2 + 1)"
    let coinView = UIImageView(image: UIImage(named:coinImageName))
    let x = bankView.center.x
    let y = bankView.center.y - 20
    coinView.center = CGPoint(x: x , y: y)
    
    // plus 1 since view's tag default is 0 includingUIViewController.view's tag is 0
    coinView.tag = number + 1
    
    coinNumbers.append(coinView.tag)
    animate(coinView: coinView)
    view.addSubview(coinView)
}

錢幣飛行動畫

這裡分步驟解釋一下,動畫都在下面這個方法裡面。

 fileprivate func animate(coinView:UIView){}

錢幣位置的移動動畫

剛才我們給 coinView 的 center 設定的是希望最後停留的位置,這裡紀錄做 targetX / targetY

  • 錢幣終點記做 targetX / targetY 這個值來自於我們前面為 coinView 設定的 center (最後要飛到存錢筒上)
  • 錢幣起點記做 fromX / fromY 在畫面底下再向下一個錢幣的位置(畫面外面)
  • 通過 CGMutablePath() 畫出移動路線。
  • 執行位置移動的動畫交給 CAKeyframeAnimation(keyPath: "position") 來做。
let targetX = coinView.layer.position.x
let targetY = coinView.layer.position.y

let path = CGMutablePath()
let fromX = CGFloat(arc4random() % 320)
let fromY = CGFloat(arc4random() % UInt32(targetY))
let height = UIScreen.main.bounds.height + coinView.frame.size.height

let cpx = targetX + (fromX - targetX)/2
let cpy = fromY / 2 - targetY

// position where animation start
path.move(to: CGPoint(x: fromX, y: height))
path.addQuadCurve(to: CGPoint(x:targetX, y:targetY), control: CGPoint(x: cpx, y: cpy))

let positionAnimation = CAKeyframeAnimation(keyPath: "position")
positionAnimation.path = path

錢幣縮小的動畫

  • 設定動畫起始大小為 1.x
  • 設定動畫最終大小為 1.x * 0.5
  • 大小變化的動畫交給 CAKeyframeAnimation(keyPath: "transform") 來執行。
// animate from big to small
let from3DScale:CGFloat = 1 + CGFloat(arc4random() % 10) * 0.1
let to3DScale:CGFloat = from3DScale * 0.5
let scaleAniamtion = CAKeyframeAnimation(keyPath: "transform")
scaleAniamtion.values = [
    CATransform3DMakeScale(from3DScale, to3DScale, from3DScale),
    CATransform3DMakeScale(to3DScale, to3DScale, to3DScale)
]
scaleAniamtion.timingFunctions = [
    CAMediaTimingFunction(name: kCAMediaTimingFunctionEaseOut),
    CAMediaTimingFunction(name: kCAMediaTimingFunctionEaseIn)
]

組合動畫

將錢幣移動和縮放大小的動畫合在一起,交給 coinView.layer

// combine animations
let animationGroup = CAAnimationGroup()
animationGroup.delegate = self
animationGroup.duration = animationDuration
animationGroup.fillMode = kCAFillModeForwards
animationGroup.isRemovedOnCompletion = false
animationGroup.animations = [positionAnimation, scaleAniamtion]
coinView.layer.add(animationGroup, forKey: "coin animation group")

動畫完成移出錢幣

CAAnimationGroup 有個代理方法,當動畫完成的時候會執行 animationDidStop()

我們通過 lastFinishedCoinNumber 來紀錄最後完成錢幣 (coinView) 動畫的 tag

通過 tag 移出對應的 coinView 並且從 coinNumbers 中移出號碼。

在所有錢幣的動畫都完成時,搖動存錢筒。

extension HomeViewController:CAAnimationDelegate {
    func animationDidStop(_ anim: CAAnimation, finished flag: Bool) {
        if flag {
            lastFinishedCoinNumber += 1
            view.viewWithTag(coinNumbers.first!)?.removeFromSuperview()
            coinNumbers.removeFirst()
            
            if lastFinishedCoinNumber == totalCoinCount {
                shakeBank()
                fireButton.isHidden = false
            }
            
        }
    }
}


Reference


上一篇
Hotel Card Layout
下一篇
3DCardLayout - 立體卡片佈局
系列文
iOS Swift x Layout x Animation x Transition30
圖片
  直播研討會
圖片
{{ item.channelVendor }} {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言