CADisplayLink 動(dòng)畫進(jìn)階
前言
之前有更新過一篇 如何實(shí)現(xiàn)一個(gè)圓形進(jìn)度條按鈕 的文章。其需求場(chǎng)景是適應(yīng)于相機(jī)拍照錄制按鈕的,主要介紹的是如何使用 CADisplayLink 實(shí)現(xiàn)其進(jìn)度條的動(dòng)畫效果。然而,寫的比較粗糙,沒有封裝也沒有整理代碼,僅僅提供實(shí)現(xiàn),dbq。于是在處女座強(qiáng)迫癥以及愧疚感的驅(qū)使下,最近又重新對(duì)該 demo 進(jìn)行了整理。
-
在這篇博客你可以了解到的內(nèi)容有:
- 如何基于 CADisplayLink 實(shí)現(xiàn)非線性動(dòng)畫效果
- 什么是緩動(dòng)函數(shù)
-
實(shí)現(xiàn)的效果舉例:
QQ20190217-141333.gif
正文
需求分析
-
做任何動(dòng)畫效果,首先要做的就是對(duì)動(dòng)畫的需求進(jìn)行拆分,比如整個(gè)動(dòng)畫的過程可以分為哪幾個(gè)部分。針對(duì)本文中要實(shí)現(xiàn)的圓形進(jìn)度條按鈕動(dòng)畫來說,整個(gè)動(dòng)畫過程可以拆分為按鈕放大的動(dòng)畫以及進(jìn)度條擴(kuò)充動(dòng)畫兩部分。
對(duì)于按鈕放大的過程,需要考慮的是在放大前到放大之后的狀態(tài),按鈕中有哪些部分發(fā)生了變化,如何發(fā)生變化,是線性過渡還是非線性過渡?
對(duì)于進(jìn)度條擴(kuò)充部分,考慮的就是黃色進(jìn)度條是如何過渡狀態(tài)的,線性還是非線性?
顯然之前的按鈕動(dòng)畫效果都是線性效果,什么是線性,就是 y = ax + b,動(dòng)畫過程是均勻變化的。非線性就是動(dòng)畫過程是根據(jù)某個(gè)函數(shù)進(jìn)行非均勻的變化。
如何基于 CADisplayLink 實(shí)現(xiàn)非線性動(dòng)畫效果
在介紹實(shí)現(xiàn)非線性動(dòng)畫效果之前,我們先簡(jiǎn)單復(fù)習(xí)一下線性動(dòng)畫效果的實(shí)現(xiàn)。假設(shè)動(dòng)畫的起始狀態(tài)變量為 fromValue,最終狀態(tài)的變量為 toValue,當(dāng)前動(dòng)畫值進(jìn)度為 percent,當(dāng)前狀態(tài)為 currentValue。那么我們可以得到計(jì)算動(dòng)畫當(dāng)前狀態(tài)的插值公式為:
currentValue = fromValue + (toValue - fromValue) * percent
其實(shí)整體實(shí)現(xiàn)思路還是和 如何實(shí)現(xiàn)一個(gè)圓形進(jìn)度條按鈕 中提到的一樣。不過在這里有需要勘誤的一個(gè)點(diǎn)是,上篇博客提到 CADisplaylink "iOS 設(shè)備屏幕顯示每秒刷新60次"。CADisplayLink 的刷新頻率確實(shí)是60次/秒左右,但是并不固定,由于每次調(diào)用 CADisplayLink 的時(shí)間間隔都不是平均的,所以我們不能根據(jù)調(diào)用次數(shù)乘以1/60的時(shí)間間隔來得到當(dāng)前經(jīng)歷的時(shí)間。正確計(jì)算當(dāng)前經(jīng)歷時(shí)間的方法是通過獲取當(dāng)前時(shí)間再減去起始時(shí)間來得到:
@property (nonatomic, assign) NSTimeInterval beginTime;
@property (nonatomic, assign) NSTimeInterval currentTime;
//在動(dòng)畫開始時(shí)記錄起始時(shí)間
self.beginTime = CACurrentMediaTime();
得到當(dāng)前的經(jīng)歷時(shí)間為
self.currentTime = CACurrentMediaTime() - self.beginTime;
由于我們是線性動(dòng)畫,所以假設(shè)動(dòng)畫時(shí)間進(jìn)程為 timePercent,timePercent = self.currentTime / duration,那么當(dāng)前動(dòng)畫值進(jìn)度 percent 就等于 timePercent,然后可以得到
currentValue = fromValue + (toValue - fromValue) * (self.currentTime / duration)
其中 fromValue、toValue、duration 都是已知數(shù),這樣我們就可以拿到 currentValue,然后在 CADisplayLink 每次調(diào)用的函數(shù)中去更新 currentValue 值,就可以實(shí)現(xiàn)線性動(dòng)畫的過渡效果了。
基于此思路我們實(shí)現(xiàn)一個(gè)插值函數(shù)如下,percent 為動(dòng)畫值進(jìn)度
- (CGFloat)interpolateFrom:(CGFloat)from to:(CGFloat)to percent:(CGFloat)percent {
return from + (to - from) * percent;
}
那么,如何實(shí)現(xiàn)非線性的動(dòng)畫效果呢? 我們先了解一下一些實(shí)現(xiàn)非線性動(dòng)畫效果的函數(shù) —— 緩動(dòng)函數(shù)
緩動(dòng)函數(shù)
緩動(dòng)函數(shù)是動(dòng)畫時(shí)間進(jìn)程(p)和動(dòng)畫值進(jìn)程(s)之間的一個(gè)映射。什么是動(dòng)畫時(shí)間進(jìn)程,就是 timePercent,而動(dòng)畫值進(jìn)程就是 percent。兩者的定義閾都為[0,1]。在線性關(guān)系中,percent = timePercent,而在非線性關(guān)系中,percent = f(timePercent)。
我們知道動(dòng)畫一般都有漸進(jìn)(EaseIn)、漸出(EaseOut)、漸近漸出(EaseInOut)的效果,它們對(duì)應(yīng)的緩動(dòng)函數(shù)圖大概是這樣:

舉例來說,對(duì)于 EaseIn 函數(shù)的圖像分析,y = x * x 的坐標(biāo)圖是完全符合 EaseIn 的效果的,所以我們可以實(shí)現(xiàn)一個(gè) y = x * x 的函數(shù)作為這種 EaseIn 效果的緩動(dòng)函數(shù):
- (double)calculate:(double) p {
return p * p;
}
那么,在線性動(dòng)畫的代碼中,調(diào)用 interpolateFrom:to:percent: 之前,將 percent(其實(shí)是 timePercent)作為參數(shù)傳入 calculate 方法,計(jì)算得到動(dòng)畫值進(jìn)程 percent,然后再調(diào)用 interpolateFrom:to:percent: 計(jì)算當(dāng)前的動(dòng)畫值。這樣我們就實(shí)現(xiàn)了非線性動(dòng)畫中的 QuadraticEaseIn 動(dòng)畫。
關(guān)于各種緩動(dòng)函數(shù)的效果可以參照這個(gè)網(wǎng)址,對(duì)于其中所有效果的緩動(dòng)函數(shù)都已經(jīng)在 Demo KiraCircleButton 中的 AnimationFunction 中實(shí)現(xiàn)了。Demo 中提供了配置頁面,可以方便的選擇配置體驗(yàn)不同的動(dòng)畫效果。
既然本文是進(jìn)階篇,那么我就不貼具體的實(shí)現(xiàn)代碼了。其中有關(guān)如何繪制,具體如何實(shí)現(xiàn)動(dòng)畫的問題,可以直接下載 Demo KiraCircleButton 看源碼。
有問題歡迎指出,謝謝。
