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

bounce

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

簡介

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

運動曲線

從初中開始,咱們就開始接觸正弦曲線、餘弦曲線,如今真的排上用場了(後悔當初數學沒學好)。咱們能夠經過對正弦餘弦作一些處理,來獲得動畫的運動曲線。彈性動畫稍微複雜一些,主要分爲兩部分,一是 波動(波形) 、二是 衰減 ,將兩者結合就能獲得咱們想要的動畫運動曲線。git

1. 淡入淡出運動曲線

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

正弦曲線

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

無論是正弦仍是餘弦,通過咱們的翻轉位移以後都能獲得一個從0到1的曲線:函數

正弦函數 0~1

餘弦函數 0~1

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

easeInOut

2. 彈性運動曲線

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

指數衰減函數

指數衰減曲線

b. 餘弦曲線 在這裏的咱們使用餘弦來做爲彈性動畫波動曲線,x倍數值越大,振動頻率 越快。
spa

餘弦振幅函數

餘弦曲線

c. 衰減的餘弦曲線 衰減函數和餘弦函數相乘,獲得初步的彈性運動曲線。3d

衰減的餘弦函數

衰減的餘弦曲線(淺色線爲衰減曲線)

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動畫實現彈性動畫的效果,對於原理實現不甚瞭解,可是一直保持着好奇心,終因而本身實現了一套方案(路子比較野)。

我的水平有限,歡迎提出建議。

相關文章
相關標籤/搜索