彈性動畫一直以來都深深地吸引我,隨著知識儲備增多,漸漸探索出一套彈性動畫的實現原理。
簡介
本文將從零開始,一步步解析彈性動畫原理,包教包會。本文Demo簡單地封裝了一個動畫庫來測試,支援UIView
的三種動畫型別:Size
、Position
、Scale
,動畫運動曲線有:bounce
、easeInOut
。CALayer
動畫暫不支援。
運動曲線
從初中開始,我們就開始接觸正弦曲線、餘弦曲線,現在真的排上用場了(後悔當初數學沒學好)。我們可以通過對正弦餘弦做一些處理,來得到動畫的運動曲線。彈性動畫稍微複雜一些,主要分為兩部分,一是 波動(波形) 、二是 衰減 ,將二者結合就能得到我們想要的動畫運動曲線。
1. 淡入淡出運動曲線
正弦曲線 ,Y
座標隨著X
座標的變化而變化,新手乍一看,這跟動畫根本沒有半毛錢關係,我們還需要進行精加工處理,才能使用。
不管是彈性動畫還是線性動畫,我們都有一個起點和終點,彈性動畫不同的是它的值在某些時候會超越最終值,然後又回到最終值。總之,我們需要一個絕對起點值為0,絕對終點值為1,progress
值範圍在0~1。舉個栗子,我們要從(100,100)
移動到(200,200)
,x和y初始值和最終值相差100,減去初始值,這0~100就是progress
的從0~1的過程。
不管是正弦還是餘弦,經過我們的翻轉位移之後都能得到一個從0到1的曲線:
這就是easeInOut
動畫的運動曲線圖,在開始和結尾比較平緩,而中間波動較大,即淡入淡出的效果。
2. 彈性運動曲線
a. 衰減曲線 彈性動畫中從0~1的過程主要由指數衰減函式來控制,指數倍數越小,衰減速度越快,在動畫引數中相當於 彈性阻尼。
b. 餘弦曲線 在這裡的我們使用餘弦來作為彈性動畫波動曲線,x
倍數值越大,振動頻率 越快。
c. 衰減的餘弦曲線 衰減函式和餘弦函式相乘,得到初步的彈性運動曲線。
d. 0~1的衰減的餘弦曲線 ,將曲線函式翻轉(加負號)後上移1(+1)即可得到最終彈性曲線,曲線從0開始,y
值隨著x
值變化波動後漸漸平穩歸於1。x
值遞增越快, 動畫速度 越快,整個動畫所需時間越短。
通過運動曲線生成動畫
1. CADisplayLink
CADisplayLink
是一個能讓我們以和螢幕重新整理率相同的頻率將動畫顯示到螢幕上的定時器。通過定時器我們利用當前動畫progress
得出運動曲線當前Y
的值,即程式碼中的timeLineY
。
舉個例子,移動position
的動畫,是用動畫的startPoint+(endPoint-startPoint)*timeLineY
=動畫當前progress
的的position
,當按順序將這些position
顯示出來就形成了我們需要的動畫。
// 新建一個displayLink
self.displayLink = [CADisplayLink displayLinkWithTarget:self selector:@selector(updateDisplayLink)];
- (void)updateDisplayLink {
// 獲取彈性動畫曲線當前進度的曲線的Y座標
float timeLineY = [self getSpringAnimation:animation springOffset:animation.progress];
// +進度
animation.progress+=animation.speed;
// 使用Y座標值 算出View當前位置
CGRect tempFrame = animationView.frame;
NSValue *fromValue = animation.fromValue;
CGPoint startPoint = fromValue.CGPointValue;
NSValue *toValue = animation.toValue;
CGPoint endPoint = toValue.CGPointValue;
tempFrame.origin.x = startPoint.x+(endPoint.x - startPoint.x)*timeLineY;
tempFrame.origin.y = startPoint.y+(endPoint.y - startPoint.y)*timeLineY;
animationView.frame = tempFrame;
}複製程式碼
下面將會提到各種動畫是如何獲取當前timeLineY
,提供了相應的曲線函式、程式碼和動畫效果圖。
2. 非曲線動畫
非曲線意思就是0~1運動軌跡是直線遞增,整個動畫會顯得比較生硬。
函式:y=x
動畫效果:
3. 淡入淡出動畫
EaseInOut曲線在動畫在起點和終點的位置遞增比較慢,動畫開啟和結束的地方比較平滑。
曲線函式:
轉換成OC程式碼:
- (CGFloat)getEaseInOutAnimation:(FDSpringAnimation *)animation springOffset:(CGFloat)x {
result = MIN(-cos(M_PI*animation.progress)/2.0+0.5, 1.000);
return result;
}複製程式碼
動畫效果:
4. 彈性動畫
彈性動畫增加了2個引數,分別是阻尼:damping
和波動頻率:frequency
。
曲線函式:
轉換成OC程式碼:
- (CGFloat)getSpringAnimation:(FDSpringAnimation *)animation springOffset:(CGFloat)x {
result = -pow(2, -animation.damping * x) * cos(animation.frequency*x)+1;
}複製程式碼
動畫效果:
擴充
上面的內容基本可以實現一個簡單的彈性動畫,本文Demo在此基礎上增加了同時多個動畫執行
、completionBlock
等功能,正在執行的動畫暫停
,移除正在執行的動畫
、替換正在執行的動畫
等功能待加入。
本文所有曲線通過Grapher
繪畫。
總結
以前一直都是直接用POP
或者UIView
動畫實現彈性動畫的效果,對於原理實現不甚瞭解,但是一直保持著好奇心,終於是自己實現了一套方案(路子比較野)。
個人水平有限,歡迎提出建議。