一、引言在iOS開發中使用動畫時,可以通過設置動畫的duration、speed、begintime、offset屬性,來設置動畫的時長、速度、起始時間及起始偏移。用一個簡單的例子來說明各個參數的的作用。動畫很簡單,一個紅色的方塊從左移到右邊。動畫的持續時間是1s,沒有重覆,效果如下。 CFTime... ...
一、引言
在iOS開發中使用動畫時,可以通過設置動畫的duration
、speed
、begintime
、offset
屬性,來設置動畫的時長、速度、起始時間及起始偏移。
用一個簡單的例子來說明各個參數的的作用。動畫很簡單,一個紅色的方塊從左移到右邊。動畫的持續時間是1s,沒有重覆,效果如下。
CFTimeInterval currentTime = CACurrentMediaTime();
CFTimeInterval currentTimeInLayer = [self.testLayer convertTime:currentTime fromLayer:nil];
CFTimeInterval addTime = currentTimeInLayer;
anim.beginTime = 0.3 + addTime;
[anim setTimeOffset:0.5];
[anim setSpeed:2];
做修改以後,效果如下:
與上面相比,三處不同
- 動畫的速度是原來的兩倍。
- 點擊開始動畫的按鈕,到開始動畫,有一個延遲。
- 動畫起始時,滑塊的位置為中央,而不是在左邊。
我們已經看到了這些屬性的效果。翻閱文檔,發現begintime
、speed
等屬性是CAMediaTiming
這協議的屬性,並且CALayer
、CAAnimation
都遵守了CAMediaTiming
協議。
那麼CAMediaTiming
協議是什麼呢?有什麼作用呢?
二、層級時間結構
根據文檔,CMediaTiming
協議構建了一個層級的時間系統,並用這個層級的時間系統來協調各個layer、animation的時間。
這個協議被CAAnimation
及CALayer
遵守,每一個遵守協議的的object對應一個time space
。根據object之間的關係,不同的time space
有層級關係。比如Layer A有一個subLayer B,那麼Layer A對應的time space
就是layer B對應的time space
的parent time space
。每一個time space
中時間的數值都是根據parent time space
的數值,以及begintime
、speed
等屬性,根據一定的規則來計算的。
為了便於理解層級時間系統,先看下layer在屏幕上的顯示位置是如何確定的,然後做一個類比。
layer層級如上。要確定sublayer1在屏幕上的顯示位置,一共分三步。
- 確定window layer在屏幕位置position1
- 根據position1及view layer的position屬性,確定view layer在屏幕中的位置position2
- 根據position2及sublayer1的position屬性,確定sublayer1在屏幕中的位置position3
與此類似,要確定sub1ayer1中的time,也要分三步。
- 確定window layer中的time1
- 根據time1及view layer的
begintime
、offset
等屬性計算出view layer中的time2 - 根據time2及sublayer1的
begintime
、offset
等屬性計算出sublayer中的time3
和確定layer的位置相比,確定時間有一些複雜,主要提現在下麵兩點
- 層級時間系統的構成複雜。
layer tree的每一級都是CALayer,而只要遵守CAMediaTiming
協議,就可以作為層級時間系統的一部分。比如CALayer
、CAAnimation
(及其子類CAAnimationGroup
)都可以作為層級時間系統的一部分。 - 不同層級之間時間轉換規則複雜
計算當前layer的位置時,只需要知道父layer的位置,以及當前layer的position屬性。計算當前層級時間時,不僅需要知道上一個層級的時間,還需要知道當前層級的begintime
、offset
、speed
等屬性。轉換的規則也比較複雜,要經歷兩次轉換。從parent time
到active local time
,再到basic local time
。
三、active local time
這次轉換是為了處理當前層級的object在父層級的的時間線上的位置,以及當前層級和父層級之間時間流逝速度的關係。
和這次轉換相關的屬性有beginTime
、speed
以及timeOffset
begin time
子層級相對於父層級的起始時間。也就是父層級的時間經過多久,子層級才開始計算時間。 比如子層級A被加入層級時間系統時,它父層級B的時間是5s,子層級A的begintime
是6s,那麼當它父層級的時間變為6s時,子層級才開始計算時間。speed
子層級相對於父層級的時間流逝速度。如果speed是2,那麼當父層級的時間增加了10s時,子層級的時間增加了20s(10s的2倍)。timeOffset
為本地時間增加一個偏移。 如果timeOffset
是5s,那麼本地時間的起始就是5s。
從parent time
到active local time
有一個公式,可以用來參考。
t = (tp - begin) * speed + offset
四、basic local time
這次轉換是為了處理當前層級的重放(repeat)、以及重放之前是否要倒放(play backward)等操作。
比如當前層級是一個動畫(CAAnimation
遵守CAMediaTiming
協議),duration
是1s,經過第一次轉換之後的active local time
是5.5s。如果動畫的repeatCount
是10,那麼經過第二次轉化以後,basic local time
會是0.5s,因此當前是動畫展示一半的狀態。
repeatCount
及repeatDuration
當前的層級要重覆的次數或重覆的時間,兩者不可同時指定。 以動畫為例,如果指定repeatCount
,那麼指定了動畫要重覆幾次。如果指定了repeatDuration
,那麼指定了動畫重覆的時間。autoreverses
在重覆之前是否要倒放。
五、文首的例子
根據這些知識,可以解釋文章開始時設置參數的效果。
當動畫被加到layer上時,動畫對應的time space
被加到層級時間系統中,是layer對應的time space
的子層級。
- 動畫的速度是原來的兩倍
設置動畫的speed
是2,這樣子動畫中的時間流逝速度時layer中時間流逝速度的2倍。當layer中時間經過0.5s時,動畫中時間已經流逝了1s,動畫已經完成了。(動畫的duration
是1s) - 點擊開始動畫的按鈕,到開始動畫,有一個延遲
我們首先得到了當前layer的時間addtime
,然後把動畫的begintime
設置為addtime+0.3
。這樣子當動畫被加到layer之後0.3s,layer中的時間是addtime+0.3
,此時動畫中的時間才開始計算,之前動畫沒有開始。 - 動畫起始時,滑塊的位置為中央,而不是在左邊
我們設置了動畫的offset
為0.5s。當動畫開始時,動畫對應的time space
的時間是0.5s,對應動畫duration
的一半,即滑塊位置在屏幕中央。
六、更多應用
瞭解了CAMediaTiming
協議後,可以實現很多動畫的效果。
- 讓某一個layer上的動畫停止
設置layer的speed
為0即可。 - 實現門打開然後關閉的效果
實現一個門打開的動畫,然後把動畫的autoreverses
屬性設置為YES
即可。 - layer上的若幹動畫依次延遲啟動
分別設置這些動畫的beginTime
為不同的值即可 - 手動控制動畫的進度
設置動畫的speed
為0,然後改變動畫的offset
即可。
蘋果已經把工具給我們了,可以做出什麼樣的產品就看大家的想象力了。
參考
控制動畫時間
控制動畫時間(上文的中文版)
Time Warp in Animation