Swift — 實作 The UX in Motion Manifesto 的動畫效果 Part.3

Jeremy Xue
7 min readJun 28, 2018

--

過了一個慵懶的週末,這次我們要繼續實作下面幾種動畫效果了,這次的主題將會提到 Value Change 和 Mask ,雖然聽起來跟動畫效果呈現沒有什麼太大的關係,但有時候透過這些效果能創造出意想不到的效果。

難得讓眼睛休息一下,又回到盯著 Mac 看的時候真是不習慣~

# Value Change( 數值變化 )

一開始看到這個效果,第一個反應是覺得這點跟「 動畫效果 」好像沒有特別關係,但我們忘了,這篇文章主要是說如何增進它的易用性,也是透過這些效果讓使用者知道它「 正在做什麼事情 」,例如我們調整一個 Slider 上的數值,如果數字沒有及時更新,你可能不知道我們正在調整數值。

下面我簡單用 Slider 來演示,我會演示兩種效果,第一個是調整後更新數值,一個是調整時動態更新數值,讓我們感覺兩者的差距:

即時、非即時的 Slider 更新效果

很明顯的看得出來靜態調整 Slider 時,數值的時候比較不容易知道目前調整到哪個比率;但動態調整時,很明顯的可以知道當下的數值是多少,讓使用者很容易更新。

我們下面簡單做一個數值變換的小遊戲,一個貓咪的體重控制,其中的貓咪體重數字變動時,圖片也會隨之改變。例如:體重在 1 ~ 10 區間為小貓;在 40 ~ 50 就為超胖貓咪,Demo 畫面如下:

貓咪體重數值變化

我們增加數值和更換圖片是用以下方法來改變的:

func valueChange() {  // 計算原體重與新體重差距值 
let value = catWeight - oldValue
Timer.scheduledTimer(withTimeInterval: 0.1, repeats: true) { (timer) in // 判斷要增加體重或是減少體重
if value > 0 {
self.oldValue += 1 } else { self.oldValue -= 1 } self.catWeightLabel.text = "Cat Weight: \(self.oldValue) kg"
// 當體重變動為 10 的倍數時,會判斷要變成哪階段的貓咪
if self.oldValue % 10 == 0 {
self.changeCatImage(value: self.oldValue) } // 當新體重等於原體重時,則停止定時器。
if self.catWeight == self.oldValue {
timer.invalidate() } }}

當然也別忘了一些小細節的處理,例如判斷是否輸入數字,或者是輸入數值是否正確,有沒有小於 0 之類等等的,詳細的我都寫在 Demo 中,隨後附上。

# Mask( 遮罩 )

有關遮罩效果如何製作的話,可以看我之前的文章:

其實遮罩效果有很多種方式可以實現,不一定要使用 mask 這個技巧,或許可以使用 clearColor 或是 clipToBounds,很多屬性調整都能達到類似的效果,端看個人想要怎麼實現它。

這邊我們參考原文內的播放器遮罩效果來實作,範例如下:

音樂播放器遮罩效果

那我們實作前先大概做一下我們的音樂播放器的版型,我們會使用一個 ImageView、TableView 和 ToolBar 來建立我們播放器界面:

播放器動畫前原型

因為我們這次動畫重點在於「 遮罩 」效果,所以其他部分的程式碼暫且不多做解釋,稍做說明就帶過。

首先看了範例的動畫效果,我們可以知道我們首先可能需要下列效果:

  1. 生成一個圖片遮罩
  2. 將圖片遮罩由長方形經由動畫效果變為圓形
  3. 唱片旋轉效果
  4. 生成一個圖片遮罩
// Make a maskmask = CALayer()mask.bounds = CGRect(x: 0, y: 0, width: albumImage.frame.height, height: albumImage.frame.height)// 我們的遮罩首先也是生成為圓形
mask.cornerRadius = mask.frame.height / 2
mask.masksToBounds = true
// 設定座標為圖片中心
mask.position = CGPoint(x: albumImage.bounds.maxX / 2, y: albumImage.bounds.maxY / 2)
mask.backgroundColor = UIColor.black.cgColor// 將圖片遮罩設為我們的 mask
albumImage.layer.mask = mask

2. 將圖片遮罩由長方形經由動畫效果變為圓形:

這邊我們因為要接續效果,所以寫了比較複雜的方法,也可以寫成個別的動畫效果。

func scaleMaskAnimation(fromValue:[Int],toValue:[Int],key:String,completion:((Bool)->Void)?){  let animation = CABasicAnimation(keyPath: "transform.scale")  animation.fromValue = fromValue
animation.toValue = toValue
animation.duration = 0.4
// 將我們動畫停留在最後的效果上
animation.fillMode = kCAFillModeBoth
animation.isRemovedOnCompletion = false
mask.add(animation, forKey: key) completion?(true)}

之後我們新增動畫效果,透過這個方法來新增縮小效果:

scaleMaskAnimation(fromValue: [3,3], toValue: [1,1], key: "small", completion: nil)

3. 唱片旋轉效果

func spinAlbumImageAnimation() {  let animation = CABasicAnimation(keyPath: "transform.rotation")  animation.duration = 4
// 每次一圈
animation.toValue = 2 * Double.pi
// 無限循環
animation.repeatCount = .infinity
self.albumImage.layer.add(animation, forKey: "spin")}

當然其他的位移效果就不多敘述,就是利用 transform 來位移和 identity 來控制位移跟回復原狀。那之後如果我們需要取消我們的動畫可以使用:

// 移除該 layer 上,該 key 值動畫
layer.removeAnimation(forKey: "動畫 key 值")
// 移除該 layer 上所有動畫
layer.removeAllAnimations()

來看一下我們播放器的 Demo:

播放器動畫效果展示

那我們這一 Part 的動畫效果教學到這邊結束,我們一樣以四個動畫效果為一個 Demo 完成後再放上 github,麻煩客官耐心等待,感謝。

--

--

Jeremy Xue
Jeremy Xue

Written by Jeremy Xue

Hi, I’m Jeremy. [好想工作室 — iOS Developer]

No responses yet