最近學習iOS動畫相關的知識,學習到控制動畫的暫定與恢復的時候,對其中的timeOffset,beginTime,fillMode等概念不太理解,遂查閱資料,學習一個。html
官方文檔中給出的暫停與恢復layer動畫的代碼以下:
ios
-(void)pauseLayer:(CALayer*)layer {
CFTimeInterval pausedTime = [layer convertTime:CACurrentMediaTime() fromLayer:nil];
layer.speed = 0.0;
layer.timeOffset = pausedTime;
}
-(void)resumeLayer:(CALayer*)layer {
CFTimeInterval pausedTime = [layer timeOffset];
layer.speed = 1.0;
layer.timeOffset = 0.0;
layer.beginTime = 0.0;
CFTimeInterval timeSincePause = [layer convertTime:CACurrentMediaTime() fromLayer:nil] - pausedTime;
layer.beginTime = timeSincePause;
}複製代碼
1. CACurrentMediaTime()是啥?bash
2. layer對象調用的convertTime: fromLayer:是啥意思?爲何要這樣作? app
3. timeOffset是啥?暫停動畫爲何要設置這個? ide
4. beginTime是啥?爲何要設置兩次beginTime?第一次設置爲0,第二次設置爲timeSincePause? 學習
5. timeSincePause是怎麼算的?動畫
mach_absolute_time()ui
mach_absolute_time()可能用到的同窗比較少,但這個概念很是重要。 spa
描述絕對時間須要找到一個均勻變化的屬性值來描述時間,CPU的時鐘週期數(ticks)恰好就是這樣的一個屬性。這個tick的數值能夠用來描述時間,而mach_absolute_time()返回的就是CPU已經運行的tick的數量。將這個tick數通過必定的轉換就能夠變成秒數,或者納秒數,這樣就和時間直接關聯了。 code
不過這個tick數,在每次手機重啓以後,會從新開始計數,並且iPhone鎖屏進入休眠以後tick也會暫停計數。
mach_absolute_time()不會受系統時間影響,只受設備重啓和休眠行爲影響。
CACurrentMediaTime()
CACurrentMediaTime()可能接觸到的同窗會多一些,先看下官方的定義:
/* Returns the current CoreAnimation absolute time. This is the result of
* calling mach_absolute_time () and converting the units to seconds. */
CFTimeInterval CACurrentMediaTime (void)複製代碼
CACurrentMediaTime()就是將上面mach_absolute_time()的CPU tick數轉化成秒數的結果。如下代碼:
double mediaTime = CACurrentMediaTime();
NSLog(@"CACurrentMediaTime: %f", mediaTime);複製代碼
返回的就是開機後設備一共運行了(設備休眠不統計在內)多少秒,另外一個API也能返回相同的值:
NSTimeInterval systemUptime = [[NSProcessInfo processInfo] systemUptime];
NSLog(@"systemUptime: %f", systemUptime);複製代碼
CACurrentMediaTime()也不會受系統時間影響,只受設備重啓和休眠行爲影響。
iOS動畫的時序的計算是理解以上代碼最關鍵的一點。在CAMediaTiming協議的timeOffset的註釋上,官方給出一下公式:
t = (tp - begin) * speed + offset
t的是就是須要計算的動畫的時間點
tp是父layer的時間點,爲了方便理解,能夠認爲是絕對時間,隨時間流逝而增長。
begin、speed、offset就是動畫的屬性beginTime、speed、timeOffset。
下面根據以上公式,來嘗試暫停和恢復動畫
默認狀況下,speed等於1,begin等於0,offset等於0。帶入等式,獲得
t = tp
若是想要暫停動畫,毫無疑問,須要將speed設置爲0。
可是若是speed等於0,上面的等式就不成立了。將speed等於0帶入上面的等式,獲得
t = offset
而offset默認等於0,動畫就回到了最初的位置了!所以,須要將暫停時的 t 的值,賦值給offset,這樣,t 的值就能夠維持在暫停的時候了。因此設置 offset = pausedTime
即
-(void)pauseLayer:(CALayer*)layer {
CFTimeInterval pausedTime = [layer convertTime:CACurrentMediaTime() fromLayer:nil];
layer.speed = 0.0;
layer.timeOffset = pausedTime;
}複製代碼
要想恢復動畫,須要作兩點:
1. 將速度調整爲1,同時將offset恢復成0,否則下次暫停時,offset就不對了。
2. t 的值要等於上面暫停時的值。由於動畫要從暫停的時候繼續往下播放
t =(tp-begin)*speed + offset
暫停的時間點仍是上面算出來的pausedTime,因此須要構造等式,使得在speed等於1,offset等於0的狀況下,使得等式左邊的 t 等於暫停時算出來的timeOffset。即
t = tp-begin = pausedTime
因此 begin 要等於 tp - pausedTime,代碼以下
-(void)resumeLayer:(CALayer*)layer {
CFTimeInterval pausedTime = [layer timeOffset];
layer.speed = 1.0;
layer.timeOffset = 0.0;
layer.beginTime = 0.0;
CFTimeInterval timeSincePause = [layer convertTime:CACurrentMediaTime() fromLayer:nil] - pausedTime;
layer.beginTime = timeSincePause;
}複製代碼
那麼,爲何要先將beginTime設置爲0,再將 beginTime 設置爲 tp - pausedTime 呢?
由於 convertTime:fromLayer: 在計算當前layer的時間時,會使用到 layer 的beginTime,
self.nicoLayer.beginTime = 0;
NSLog(@"beginTime1 = %f", self.nicoLayer.beginTime);
CFTimeInterval tp1 = [self.nicoLayer convertTime:CACurrentMediaTime() fromLayer:nil];
NSLog(@"tp1 = %f", tp1);
self.nicoLayer.beginTime = 10;
NSLog(@"beginTime2 = %f", self.nicoLayer.beginTime);
CFTimeInterval tp2 = [self.nicoLayer convertTime:CACurrentMediaTime() fromLayer:nil];
NSLog(@"tp2 = %f", tp2);複製代碼
打印結果:
beginTime1 = 0.000000
tp1 = 81431.807847
beginTime2 = 10.000000
tp2 = 81421.808063複製代碼
因而可知,convertTime:fromLayer: 方法在計算的時候,會使用到上面的公式,因此須要將beginTime先恢復成0,再進行計算,才能獲得正確的時間。
1. beginTime爲正的時候,計算動畫時間的時候,時間點會往前位移beginTime秒
2. timeOffset爲正的時候,計算動畫時間的時候,時間點會日後位移timeOffset秒
1. CACurrentMediaTime()是啥?
答:CACurrentMediaTime()就是將上面mach_absolute_time()的CPU tick數轉化成秒數的結果。CACurrentMediaTime()也不會受系統時間影響,只受設備重啓和休眠行爲影響。
2. layer對象調用的convertTime: fromLayer:是啥意思?爲何要這樣作?
答:計算當前layer的絕對時間
3. timeOffset是啥?暫停動畫爲何要設置這個?
答:動畫時間點的計算知足一個公式,t = (tp - begin) * speed + offset,設置timeOffset使得公式在speed等於0的狀況下,動畫的時間點(或位置)等於暫停時的時間(或位置)
4. beginTime是啥?爲何要設置兩次beginTime?第一次設置爲0,第二次設置爲timeSincePause?
答:同上,是爲了在speed設置爲1的狀況下,使等式成立。
5. timeSincePause是怎麼算的?
答:tp是當前時間點,pausedTime是暫停時的時間點,pausedTime是固定不變的,tp隨時間流逝而增長,因此timeSincePause是從暫停到當前時間的間隔時長,beginTime設置爲timeSincePause,即將動畫向前位移到上次停下來的時間點。
Apple Document: Pausing and Resuming Animations
Stack Overflow: Comprehend pause and resume animation on a layer