彈性動畫一直以來都深深地吸引我,隨着知識儲備增多,漸漸探索出一套彈性動畫的實現原理。javascript
本文將從零開始,一步步解析彈性動畫原理,包教包會。本文Demo簡單地封裝了一個動畫庫來測試,支持UIView
的三種動畫類型:Size
、Position
、Scale
,動畫運動曲線有:bounce
、easeInOut
。CALayer
動畫暫不支持。java
從初中開始,咱們就開始接觸正弦曲線、餘弦曲線,如今真的排上用場了(後悔當初數學沒學好)。咱們能夠經過對正弦餘弦作一些處理,來獲得動畫的運動曲線。彈性動畫稍微複雜一些,主要分爲兩部分,一是 波動(波形) 、二是 衰減 ,將兩者結合就能獲得咱們想要的動畫運動曲線。git
正弦曲線 ,Y
座標隨着X
座標的變化而變化,新手乍一看,這跟動畫根本沒有半毛錢關係,咱們還須要進行精加工處理,才能使用。
github
無論是彈性動畫仍是線性動畫,咱們都有一個起點和終點,彈性動畫不一樣的是它的值在某些時候會超越最終值,而後又回到最終值。總之,咱們須要一個絕對起點值爲0,絕對終點值爲1,progress
值範圍在0~1。舉個栗子,咱們要從(100,100)
移動到(200,200)
,x和y初始值和最終值相差100,減去初始值,這0~100就是progress
的從0~1的過程。spring
無論是正弦仍是餘弦,通過咱們的翻轉位移以後都能獲得一個從0到1的曲線:函數
這就是easeInOut
動畫的運動曲線圖,在開始和結尾比較平緩,而中間波動較大,即淡入淡出的效果。
測試
a. 衰減曲線 彈性動畫中從0~1的過程主要由指數衰減函數來控制,指數倍數越小,衰減速度越快,在動畫參數中至關於 彈性阻尼。動畫
b. 餘弦曲線 在這裏的咱們使用餘弦來做爲彈性動畫波動曲線,x
倍數值越大,振動頻率 越快。
spa
c. 衰減的餘弦曲線 衰減函數和餘弦函數相乘,獲得初步的彈性運動曲線。3d
d. 0~1的衰減的餘弦曲線 ,將曲線函數翻轉(加負號)後上移1(+1)便可獲得最終彈性曲線,曲線從0開始,y
值隨着x
值變化波動後漸漸平穩歸於1。x
值遞增越快, 動畫速度 越快,整個動畫所需時間越短。
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
,提供了相應的曲線函數、代碼和動畫效果圖。
非曲線意思就是0~1運動軌跡是直線遞增,整個動畫會顯得比較生硬。
函數:y=x
動畫效果:
EaseInOut曲線在動畫在起點和終點的位置遞增比較慢,動畫開啓和結束的地方比較平滑。
曲線函數:
- (CGFloat)getEaseInOutAnimation:(FDSpringAnimation *)animation springOffset:(CGFloat)x {
result = MIN(-cos(M_PI*animation.progress)/2.0+0.5, 1.000);
return result;
}複製代碼
動畫效果:
彈性動畫增長了2個參數,分別是阻尼:damping
和波動頻率:frequency
。
曲線函數:
- (CGFloat)getSpringAnimation:(FDSpringAnimation *)animation springOffset:(CGFloat)x {
result = -pow(2, -animation.damping * x) * cos(animation.frequency*x)+1;
}複製代碼
動畫效果:
上面的內容基本能夠實現一個簡單的彈性動畫,本文Demo在此基礎上增長了同時多個動畫運行
、completionBlock
等功能,正在運行的動畫暫停
,移除正在運行的動畫
、替換正在運行的動畫
等功能待加入。
本文全部曲線經過Grapher
繪畫。
之前一直都是直接用POP
或者UIView
動畫實現彈性動畫的效果,對於原理實現不甚瞭解,可是一直保持着好奇心,終因而本身實現了一套方案(路子比較野)。
我的水平有限,歡迎提出建議。