包教包會 - 彈性動畫的原理與實現

xietao3發表於2017-06-08

包教包會 - 彈性動畫的原理與實現
bounce

彈性動畫一直以來都深深地吸引我,隨著知識儲備增多,漸漸探索出一套彈性動畫的實現原理。

簡介

本文將從零開始,一步步解析彈性動畫原理,包教包會。本文Demo簡單地封裝了一個動畫庫來測試,支援UIView的三種動畫型別:SizePositionScale,動畫運動曲線有:bounceeaseInOutCALayer動畫暫不支援。

運動曲線

從初中開始,我們就開始接觸正弦曲線、餘弦曲線,現在真的排上用場了(後悔當初數學沒學好)。我們可以通過對正弦餘弦做一些處理,來得到動畫的運動曲線。彈性動畫稍微複雜一些,主要分為兩部分,一是 波動(波形) 、二是 衰減 ,將二者結合就能得到我們想要的動畫運動曲線。

1. 淡入淡出運動曲線

正弦曲線 Y座標隨著X座標的變化而變化,新手乍一看,這跟動畫根本沒有半毛錢關係,我們還需要進行精加工處理,才能使用。

包教包會 - 彈性動畫的原理與實現
正弦曲線

不管是彈性動畫還是線性動畫,我們都有一個起點和終點,彈性動畫不同的是它的值在某些時候會超越最終值,然後又回到最終值。總之,我們需要一個絕對起點值為0,絕對終點值為1,progress值範圍在0~1。舉個栗子,我們要從(100,100)移動到(200,200),x和y初始值和最終值相差100,減去初始值,這0~100就是progress的從0~1的過程。

不管是正弦還是餘弦,經過我們的翻轉位移之後都能得到一個從0到1的曲線:

包教包會 - 彈性動畫的原理與實現
正弦函式 0~1

包教包會 - 彈性動畫的原理與實現
餘弦函式 0~1

這就是easeInOut動畫的運動曲線圖,在開始和結尾比較平緩,而中間波動較大,即淡入淡出的效果。

包教包會 - 彈性動畫的原理與實現
easeInOut

2. 彈性運動曲線

a. 衰減曲線 彈性動畫中從0~1的過程主要由指數衰減函式來控制,指數倍數越小,衰減速度越快,在動畫引數中相當於 彈性阻尼

包教包會 - 彈性動畫的原理與實現
指數衰減函式

包教包會 - 彈性動畫的原理與實現
指數衰減曲線

b. 餘弦曲線 在這裡的我們使用餘弦來作為彈性動畫波動曲線,x倍數值越大,振動頻率 越快。

包教包會 - 彈性動畫的原理與實現
餘弦振幅函式

包教包會 - 彈性動畫的原理與實現
餘弦曲線

c. 衰減的餘弦曲線 衰減函式和餘弦函式相乘,得到初步的彈性運動曲線。

包教包會 - 彈性動畫的原理與實現
衰減的餘弦函式

包教包會 - 彈性動畫的原理與實現
衰減的餘弦曲線(淺色線為衰減曲線)

d. 0~1的衰減的餘弦曲線 ,將曲線函式翻轉(加負號)後上移1(+1)即可得到最終彈性曲線,曲線從0開始,y值隨著x值變化波動後漸漸平穩歸於1。x值遞增越快, 動畫速度 越快,整個動畫所需時間越短。

包教包會 - 彈性動畫的原理與實現
0~1的衰減的餘弦函式

包教包會 - 彈性動畫的原理與實現
0~1的衰減的餘弦曲線

通過運動曲線生成動畫

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

動畫效果:

包教包會 - 彈性動畫的原理與實現
line_position.gif

包教包會 - 彈性動畫的原理與實現
line_scale.gif

3. 淡入淡出動畫

EaseInOut曲線在動畫在起點和終點的位置遞增比較慢,動畫開啟和結束的地方比較平滑。

曲線函式:

包教包會 - 彈性動畫的原理與實現
餘弦函式 0~1

轉換成OC程式碼:

- (CGFloat)getEaseInOutAnimation:(FDSpringAnimation *)animation springOffset:(CGFloat)x {
    result = MIN(-cos(M_PI*animation.progress)/2.0+0.5, 1.000);
    return result;
}複製程式碼

動畫效果:

包教包會 - 彈性動畫的原理與實現
easeInOut_position.gif

包教包會 - 彈性動畫的原理與實現
easeInOut_scale.gif

4. 彈性動畫

彈性動畫增加了2個引數,分別是阻尼:damping和波動頻率:frequency

曲線函式:

包教包會 - 彈性動畫的原理與實現
0~1的衰減的餘弦函式

轉換成OC程式碼:

- (CGFloat)getSpringAnimation:(FDSpringAnimation *)animation springOffset:(CGFloat)x {
    result = -pow(2, -animation.damping * x) * cos(animation.frequency*x)+1;
}複製程式碼

動畫效果:

包教包會 - 彈性動畫的原理與實現
bounce_position.gif

包教包會 - 彈性動畫的原理與實現
bounce_scale.gif

擴充

上面的內容基本可以實現一個簡單的彈性動畫,本文Demo在此基礎上增加了同時多個動畫執行completionBlock等功能,正在執行的動畫暫停移除正在執行的動畫替換正在執行的動畫等功能待加入。

本文所有曲線通過Grapher繪畫。

包教包會 - 彈性動畫的原理與實現
Grapher

總結

以前一直都是直接用POP或者UIView動畫實現彈性動畫的效果,對於原理實現不甚瞭解,但是一直保持著好奇心,終於是自己實現了一套方案(路子比較野)。

個人水平有限,歡迎提出建議。

相關文章