概述在動畫中,我們會指定動畫的持續時間。例如scaleAnimation.duration = self.config.appearDuration那麼這個時間是怎麼定義的呢?是指的絕對時間嗎?層級時間結構layer在屏幕上的顯示位置是根據父layer的位置以及本身相對於父layer偏移定義的。與此... ...
概述
在動畫中,我們會指定動畫的持續時間。例如
scaleAnimation.duration = self.config.appearDuration
那麼這個時間是怎麼定義的呢?是指的絕對時間嗎?
層級時間結構
layer在屏幕上的顯示位置是根據父layer的位置以及本身相對於父layer偏移定義的。
與此類似,每一個layer都有自己的time space,計算本地時間(local time)時候,需要根據父layer的時間以及一定的轉換規則來計算出本地時間。
這個規則就是CAMediaTiming
協議。每一個CALayer
和CAAnimation
實現了這個協議。


關於時間的概念
- 絕對時間
Absolute time
由CACurrentMediaTime
函數返回,實際調用mach_absolute_time()
。 active local time
根據CAMediaTiming
協議計算得到的當前對象上的時間。basic local time
由於動畫可以重覆(repeat)或者回放(play backwards)。需要把active local time
轉化為做動畫相關的時間。
例如active local time
是5.5s,動畫的重覆次數是10,動畫持續時間是1s。那麼5.5s的active local time
對應的local time是0.5s。
CAMediaTiming
協議
beginTime
Required. Specifies the begin time of the receiver in relation to its parent object, if applicable.
指定了指定了父對象時間和子對象時間的偏移。
speed
Specifies how time is mapped to receiver’s time space from the parent time space
對動畫以及子動畫速度應用一個縮放的因數。如果speed是2.0,那麼本地時間流逝的速度是父對象的時間流逝速度的兩倍。
timeOffset
Required. Specifies an additional time offset in active local time.
對本地時間做了一個偏移。
時間轉換公式
- 從父layer轉化為active local time
\[ t= max\left\{(t_p-begintime),0\right\}*speed+offset \]
其中\(t\)是本地時間,\(t_p\)是父layer的時間,其他都是
CAMediaTiming
要求實現的欄位。
例子
用一個簡單的例子來說明各個參數的影響。動畫很簡單,一個紅色的方塊從左移到右邊。動畫的持續時間是1s,沒有重覆。

- 設置speed為2,begin time為0.3s,offset為0.5s,效果如下

與上面相比,三處不同- 動畫的速度是原來的兩倍,這是因為動畫的speed是2。
- 動畫起始時,滑塊的位置為中央,而不是在左邊。這是因為offset為0.5s。由於動畫的持續時間是1s,0.5s時,動畫剛剛進行了一半,滑塊的位置是在屏幕中央。
- 點擊開始動畫的按鈕,到開始動畫,有一個延遲,這是因為begin time的時間不是預設值,而是有一個0.3s的延遲。
- 時間變換的圖像表示
- 從父layer的時間到子layer的active local time

圖中,直線的斜率是speed,第一個y值不為零的點,對應的橫軸坐標是begintime,對應的y軸坐標是offset - 從active local time到basic local time

圖中,不為0的部分的x軸長度,即是動畫時間,由repeattime或repeatDuration指定。由於這個動畫沒有repeattime或repeatDuration,因此就是動畫的duration。如果指定了動畫的時間,比如repeatcount為3,那麼非0部分會重覆3次。
如果指定了autoreverses
為yes,那麼折線會部分有負的曲率。
第一個不為0的點對應的橫軸坐標即為offset。fillMode
可以理解為不在動畫時間內的y值是什麼。如果kCAFillModeBackwards
,對應於橫軸在offset之前時,縱軸對應於offset。kCAFillModeForwards
對應於橫軸在動畫結束之後,縱軸保存不變。
- 從父layer的時間到子layer的active local time
- 父對象和子對象聯動
我們的例子中,動畫是加在layer上的,它們都遵守CAMediatiming
協議,就CAMediatiming
看來,動畫的父對象是layer。- 設置父對象的speed
我們設置layer的speed為2,動畫的speed為0.1,實際的速度會是0.2.  - 設置父對象的offset
設置父對象的offset為0.5,那麼動畫將會在一半處開始。

- 設置父對象的speed
關於begintime
根據公式
\[
t= max\left\{(t_p-begintime),0\right\}*speed+offset
\]
這裡begintime是應該怎麼指定呢?如果想把一個加到layer的動畫的延遲5s執行,應該把begintime直接設為5嗎?
由於begintime是相對於父對象(layer)的時間偏移。由於layer可能在很久以前就存在了,因此對於動畫來說\(t_p\)是一個很大的值。直接把begintime指定為5s,那麼\(t\)將會是一個很大的值。正確的做法是把begintime設置為5s+這個layer被加到父layer以後,度過的時間,稱為addtime.
animation.beginTime = addTime + delay;
如何得到addtime
addTime = [layer convertTime:CACurrentMediaTime() fromLayer:nil];
參考
控制動畫時間
控制動畫時間
Time Warp in Animation