#82 利用 CABasicAnimation & CAShapeLayer 繪製線條動畫

利用 CAShapeLayer & UIBezierPath 我們可以繪製線條,不過不僅於此,當我們結合 CABasicAnimation 時,我們還能讓它變成小畫家,以生動的動畫一筆一劃繪製線條呢。

比方我們可參考以下連結 Ting 同學繪製的 Apple,一筆一劃畫出美味的蘋果咬一口。

接下來就讓我們示範如何利用 CABasicAnimation 繪製帥氣的勾勾吧。

以 CAShapeLayer 繪製帥氣的勾勾

override func viewDidLoad() {
super.viewDidLoad()
let path = UIBezierPath()
var point = CGPoint(x: 10, y: 260)
path.move(to: point)
point = CGPoint(x: 60, y: 300)
path.addLine(to: point)
point = CGPoint(x: 140, y: 160)
path.addLine(to: point)
let checkmarkLayer = CAShapeLayer()
checkmarkLayer.path = path.cgPath
checkmarkLayer.lineWidth = 5
checkmarkLayer.strokeColor = UIColor.red.cgColor
checkmarkLayer.fillColor = nil
view.layer.addSublayer(checkmarkLayer)
}

關於如何利用 CAShapeLayer & UIBezierPath 繪製線條,相關說明可參考以下連結的方法 2,addSublayer。

若想查詢線條的座標位置,可將圖片上傳到網站 iview,參考以下連結的教學。

使用 CABasicAnimation 設定動畫

let animation = CABasicAnimation(keyPath: "strokeEnd")
animation.fromValue = 0
animation.toValue = 1
animation.duration = 5
checkmarkLayer.add(animation, forKey: nil)

結果

說明:

let animation = CABasicAnimation(keyPath: "strokeEnd")

CABasicAnimation 可以實現許多動畫效果,比方透明度的變化,位置大小的變化等,而動畫的效果則由我們生成 CABasicAnimation 時傳入的 keyPath 決定。此處我們傳入 strokeEnd,實現從 CABasicAnimation 路徑的起點畫線到終點的動畫效果。

animation.fromValue = 0
animation.toValue = 1

控制動畫從哪裡開始跟結束。我們的動畫將作用於 CAShapeLayer,fromValue 設為 0 表示它將從 CAShapeLayer 路徑的起點開始,toValue 設為 1 表示動畫將在畫到路徑終點時結束。

讓我們調整一下數值,進一步了解它的作用。比方我們將 fromValue 設為 0.2, toValue 設為 0.8,此時將如下圖,動畫在線條已經畫了一部分時才開始,在還沒畫到終點時動畫就被狠心地終止了。

animation.fromValue = 0.2
animation.toValue = 0.8
animation.duration = 5

設定動畫的時間,以秒為單位。

checkmarkLayer.add(animation, forKey: nil)

將動畫加到 CAShapeLayer 上,實現繪製 CAShapeLayer 線條的動畫效果。

了解基本的動畫繪製方法後,接下來我們改用一開始 Ting 同學繪製的 Apple 為例,進一步說明其它進階的效果。

動畫的速度

設定 timingFunction 調整動畫的速度,比方 easeInEaseOut 表示一開始比較慢,過程中逐漸加速,接近結尾時再放慢速度。

animation.timingFunction = CAMediaTimingFunction(name: .easeInEaseOut)

關於其它可設定的速度名字,可參考以下連結。

橡皮擦清除線條的動畫效果

生成 CABasicAnimation 時傳入 strokeStart 將實現橡皮擦清除線條的動畫效果。

let animation = CABasicAnimation(keyPath: "strokeStart")

如下圖所示,一開始 Apple 會先完整呈現,然後再照著當初繪製路徑的順序消除。

重覆的動畫

CABasicAnimation 的 repeatCount 可以控制動畫重覆的次數,比方 2 表示動畫將重覆兩次。那如果希望動畫永遠不要停,一直到世界末日呢 ?

沒問題,只要設定 Float.greatestFiniteMagnitude 即可 !

animation.repeatCount = .greatestFiniteMagnitude

比方以下為永不結束的畫圓(ring)動畫,半夜睡不著時可以看著它幫助入眠。

let circlePath = UIBezierPath(roundedRect: CGRect(x: 50, y: 100, width: 100, height: 100), cornerRadius: 50)
let circleLayer = CAShapeLayer()
circleLayer.path = circlePath.cgPath
circleLayer.lineWidth = 5
circleLayer.strokeColor = UIColor.red.cgColor
circleLayer.fillColor = nil
view.layer.addSublayer(circleLayer)
let animation = CABasicAnimation(keyPath: "strokeEnd")
animation.fromValue = 0
animation.toValue = 1
animation.duration = 1.5
animation.repeatCount = .greatestFiniteMagnitude
circleLayer.add(animation, forKey: nil)

動畫結束時執行的程式

override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view.

let path = UIBezierPath()
var point = CGPoint(x: 10, y: 260)
path.move(to: point)
point = CGPoint(x: 60, y: 300)
path.addLine(to: point)
point = CGPoint(x: 140, y: 160)
path.addLine(to: point)
path.close()
let checkmarkLayer = CAShapeLayer()
checkmarkLayer.path = path.cgPath
checkmarkLayer.lineWidth = 5
checkmarkLayer.strokeColor = UIColor.red.cgColor
checkmarkLayer.fillColor = UIColor.clear.cgColor
view.layer.addSublayer(checkmarkLayer)

CATransaction.begin()
CATransaction.setCompletionBlock {
checkmarkLayer.fillColor = UIColor.blue.cgColor
}
let animation = CABasicAnimation(keyPath: "strokeEnd")
animation.fromValue = 0
animation.toValue = 1
animation.duration = 5
checkmarkLayer.add(animation, forKey: nil)
CATransaction.commit()
}

利用 CATransaction.setCompletionBlock 設定動畫結束時執行的程式,在將整個動畫的程式包在 CATransaction.begin() & CATransaction.commit() 之間。

文字動畫

參考 Yuki 同學繪製的文字動畫。

作品集

--

--

彼得潘的 iOS App Neverland
彼得潘的 100 道 Swift iOS App 謎題

彼得潘的iOS App程式設計入門,文組生的iOS App程式設計入門講師,彼得潘的 Swift 程式設計入門,App程式設計入門作者,http://apppeterpan.strikingly.com