這是我第一次翻譯國外大神的文章避诽。為了行文通順龟虎,某些地方?jīng)]有完全遵照原文。末尾附有自己的一些私貨沙庐。
原文鏈接如下:
http://ronnqvi.st/controlling-animation-timing/
如有疏漏鲤妥,敬請指出,不勝感激拱雏。
概述
在iOS的動畫框架中棉安,有一個叫做CAMediaTiming的協(xié)議,它由CAAnimation實現(xiàn)铸抑,而后者是CABasicAnimation和CAKeyframeAnimation的基類贡耽。所有和時序相關(guān)的屬性:duration, beginTime, repeatCount等等都是從這個協(xié)議中來的∪笛矗總體來說蒲赂,該協(xié)議定義了8個屬性,這8個屬性可以通過不同的組合方式來精確控制動畫時序刁憋。蘋果官方文檔對每個屬性的介紹都非常簡略滥嘴,因此閱讀官文會比閱讀這篇博客更快,但是我個人認為至耻,有關(guān)時序的問題若皱,最好還是結(jié)合圖表來理解。因此花費一些篇幅是有必要的有梆。
圖解CAMediaTiming
為了解釋和時序有關(guān)的各個屬性是尖,我現(xiàn)在要做一個顏色變化的動畫(橙->藍)。下圖中的格子表示一段動畫從開始到結(jié)束的整個時間線泥耀,而其中每一格代表時間線中的一秒饺汹。你可以看到整個動畫過程中任意時刻的顏色。
duration
下圖展示了設(shè)置duration=1.5時的情景痰催。
CAAnimation會在動畫結(jié)束后默認從layer上移除兜辞,該過程同樣如圖上所示。一旦animation對象到達了它的“最終值”(animation的值通過“插值”法進行變化夸溶。關(guān)于“最終值”的解釋會附在文末逸吵。——譯者注)缝裁,它就會從layer上移除扫皱。如果layer原本的顏色是橙色(注意,不是說它的fromValue是orangeColor——譯者注),那么在動畫結(jié)束后layer將變回橙色韩脑。在該圖示中氢妈,layer的原本顏色是白色,所以在動畫添加到layer上1.5秒后段多,layer就會變成白色首量。
beginTime
如果我們再加上beginTime這個屬性會更好理解一些。如圖:
duration被設(shè)置為1.5秒进苍,beginTime被設(shè)置為currentTime(CACurrentMediaTime() + 1)加缘,所以動畫將在Animation對象被添加到layer上1秒后動畫開始執(zhí)行。2.5秒后結(jié)束觉啊。
如果要讓layer在動畫開始前顯示Animation的fromValue拣宏,你可以設(shè)置fillMode為kCAFillModeBackwards,來讓動畫“向后填充”杠人。圖示如下:
autoreverses
autoreverses屬性可以讓動畫從“起始值”執(zhí)行到“最終值”蚀浆,然后再反過來,從“終止值”執(zhí)行到“起始值”搜吧。因此,整個動畫執(zhí)行時間將會是duration 的2倍杨凑。
repeatCount
repeatCount可以將動畫重復(fù)執(zhí)行2次(如下圖所示)甚至任意次(你可以設(shè)置repeatCount為1.5滤奈,讓動畫執(zhí)行一次半)。一旦動畫執(zhí)行到了“最終值”它會立即回到“起始值”然后重新開始執(zhí)行撩满。請讀者將autoreverses和repeatCount屬性做一個對比蜒程。
repeatDuration
repeatDuration作用和repeatCount類似,但是卻很少用到伺帘。它會單純地在給定的時間內(nèi)(盡可能地)重復(fù)動畫昭躺。下圖所示為repeatCount=2時的情景。如果repeatDuration小于duration伪嫁,則動畫會提前結(jié)束(即動畫的真正執(zhí)行時間取決于repeatCount领炫。——譯者注)张咳。
以上屬性都可以組合使用帝洪。
speed
speed是一個更為有趣的時序?qū)傩浴H绻鹍uration=3脚猾,speed=2葱峡,則動畫執(zhí)行時間會是1.5秒。(即執(zhí)行時間=speed * duration)龙助。
如果只是為了控制一個簡單動畫的速度砰奕,你當(dāng)然可以通過設(shè)置beginTime和duration來達到目的。但是speed屬性的強大之處在于:
- 動畫的速度具備“等級”關(guān)系。
- CAAnimation不是唯一一個實現(xiàn)了CAMediaTiming的類军援。
速度的“等級關(guān)系”
如果一個動畫組(Animation Group)的speed為2仅淑,而其中一個動畫的speed為1.5,那么該動畫將會以三倍的速度播放盖溺。
CAMediaTiming的其他應(yīng)用
CAMediaTiming不僅被CAAnimation類實現(xiàn)漓糙,也同樣被CALayer實現(xiàn)。后者是所有核心動畫圖層的基類烘嘱。因此昆禽,對CALayer的speed賦值將會影響其上面添加的所有動畫。最終speed = layer.speed * animation.speed蝇庭。
動畫暫停
通過設(shè)置speed屬性為0醉鳖,我們可以暫停一個動畫。timeOffset屬性為我們提供了一個可以從外部控制動畫進程的機制哮内。例如盗棵,可以通過slider來查看動畫的每一個時刻的樣子。
timeOffset屬性乍一看會很奇怪北发。顧名思義纹因,該屬性用于抵消計算動畫狀態(tài)的時間。我們最好通過圖畫來解釋琳拨。下圖表示一個duration=3瞭恰,timeOffset=1的動畫。
該動畫直接調(diào)到“橙色->藍色”這個動畫過程的第1秒處開始執(zhí)行(此時它已經(jīng)不是純橙色了——譯者注)狱庇,直到2秒時完全變?yōu)樗{色惊畏。隨后,動畫跳轉(zhuǎn)到其純橙色然后執(zhí)行其第0秒第1秒的動畫過程密任。也就是說颜启,timeOffset將動畫01秒的過程抽取了出來,放到最后執(zhí)行浪讳。
這個屬性本身沒什么用處缰盏,但如果同時把speed設(shè)置為0,我們就可以控制動畫的“當(dāng)前狀態(tài)”驻债。被暫停的動畫將會卡在第一幀上乳规,如果你看上圖所示的帶偏移量的動畫最開始的顏色,你會發(fā)現(xiàn)它其實是顏色變化開始1秒后應(yīng)該呈現(xiàn)的顏色合呐。通過將timeOffset設(shè)置為其他值暮的,你可以看到動畫該時刻的樣子。
控制動畫時序
speed和timeOffset結(jié)合使用可以控制動畫的“當(dāng)前”時間淌实。如下所示是一個slider的實例冻辩,我們通過設(shè)置其timeOffset來查看方塊的顏色變化猖腕。
Slider
這個例子非常簡單,創(chuàng)建一個basic animation恨闪,加載到一個layer上倘感。我們把動畫的speed設(shè)置為0,讓它停下咙咽。
CABasicAnimation *changeColor =
[CABasicAnimation animationWithKeyPath:@"backgroundColor"];
changeColor.fromValue = (id)[UIColor orangeColor].CGColor;
changeColor.toValue = (id)[UIColor blueColor].CGColor;
changeColor.duration = 1.0; // For convenience
[self.myLayer addAnimation:changeColor
forKey:@"Change color"];
self.myLayer.speed = 0.0; // Pause the animation
然后在action方法中我們將slider的value賦值給做動畫的layer的timeOffset屬性:
- (IBAction)sliderChanged:(UISlider *)sender {
self.myLayer.timeOffset = sender.value; // Update "current time"
}
效果圖:
小結(jié)
- 通過設(shè)置fillMode為forward老玛,并將removedOnCompletion設(shè)為NO,可以讓圖層保持在動畫結(jié)束以后的狀態(tài)钧敞。但是注意蜡豹,animation對象只會影響到圖層的PresentationLayer,而對ModelLayer沒有影響溉苛。
- 如果對speed屬性賦負值镜廉,動畫會倒著執(zhí)行。
私貨
關(guān)于timeOffset
蘋果官方文檔的解釋:
Specifies an additional time offset in active local time.
翻譯過來就是“在本地活躍時間中指定一個額外的時間偏移量”愚战。
聽起來還是很費解是吧娇唯?其實timeOffset就是說,把動畫時序中開頭的某個時間段分割出來寂玲,“拼接”到動畫的末尾塔插。由于這個操作改變了動畫真正的起點,因此拓哟,動畫看起來就從分割處開始執(zhí)行了佑淀。
animation的插值法
對于動畫的“值”,蘋果給出了三個屬性彰檬,分別是fromValue, toValue, byValue。蘋果規(guī)定谎砾,最多可以同時給這三個屬性中的兩個賦值逢倍。運行時,動畫在“初始值”和“最終值”之間進行插值景图,從而確定各個時刻Layer的狀態(tài)较雕。初始值不一定是fromValue哦!
插值策略如下:
- 賦值給fromValue和toValue:在fromValue挚币、toValue之間插值亮蒋。
- 賦值給fromValue和byValue:在fromValue、fromValue + byValue之間插值妆毕。
- 賦值給B和toValue:在toValue-byValue慎玖、toValue之間插值。
- 賦值給fromValue:在fromValue和該屬性當(dāng)前的value之間插值笛粘。
- 賦值給toValue:在keypath屬性的presentationlayer的當(dāng)前值和toValue之間插值趁怔。
- 賦值給byValue:在keypath屬性的presentationlayer的當(dāng)前值和byValue之間插值湿硝。
- 所有的都為nil:在keypath屬性的presentationlayer先前的值和現(xiàn)在的值之間插值。
對于最后一條润努,我自己也沒有嘗試成功過关斜。不知是不是個人理解出了偏差。這里先附上官文原文:
All properties are nil. Interpolates between the previous value of keyPath in the target layer’s presentation layer and the current value of keyPath in the target layer’s presentation layer.
歡迎各位大神指點铺浇!