版本記錄
版本號(hào) | 時(shí)間 |
---|---|
V1.0 | 2017.09.23 |
前言
app中好的炫的動(dòng)畫(huà)可以讓用戶耳目一新,為產(chǎn)品增色不少,關(guān)于動(dòng)畫(huà)的實(shí)現(xiàn)我們可以用基本動(dòng)畫(huà)、關(guān)鍵幀動(dòng)畫(huà)、序列幀動(dòng)畫(huà)以及基于CoreGraphic的動(dòng)畫(huà)等等,接下來(lái)這幾篇我就介紹下我可以想到的幾種動(dòng)畫(huà)繪制方法。具體Demo示例已開(kāi)源到Github —— 刀客傳奇,感興趣的可以看我寫的另外幾篇。
1. 實(shí)現(xiàn)動(dòng)畫(huà)方式深度解析(一) —— 播放GIF動(dòng)畫(huà)(一)
2. 實(shí)現(xiàn)動(dòng)畫(huà)方式深度解析(二) —— 播放GIF動(dòng)畫(huà)之框架FLAnimatedImage的使用(二)
3. 實(shí)現(xiàn)動(dòng)畫(huà)方式深度解析(三) —— 播放序列幀動(dòng)畫(huà)(一)
4. 實(shí)現(xiàn)動(dòng)畫(huà)方式深度解析(四) —— QuartzCore框架(一)
5. 實(shí)現(xiàn)動(dòng)畫(huà)方式深度解析(五) —— QuartzCore框架之CoreAnimation(二)
6. 實(shí)現(xiàn)動(dòng)畫(huà)方式深度解析(六) —— Core Animation Basics(三)
7. 實(shí)現(xiàn)動(dòng)畫(huà)方式深度解析(七) —— Core Animation之Setting Up Layer Objects(四)
8. 實(shí)現(xiàn)動(dòng)畫(huà)方式深度解析(八) —— Core Animation之動(dòng)畫(huà)層內(nèi)容 (五)
9. 實(shí)現(xiàn)動(dòng)畫(huà)方式深度解析(九) —— Core Animation之構(gòu)建圖層層級(jí) (六)
Advanced Animation Tricks - 高級(jí)動(dòng)畫(huà)技巧
有很多方法可以配置基于屬性或關(guān)鍵幀的動(dòng)畫(huà),為您做更多的事情。 需要一起或順序執(zhí)行多個(gè)動(dòng)畫(huà)的應(yīng)用程序可以使用更高級(jí)的行為來(lái)同步這些動(dòng)畫(huà)的時(shí)間或?qū)⑺鼈冩溄釉谝黄稹?您還可以使用其他類型的動(dòng)畫(huà)對(duì)象來(lái)創(chuàng)建視覺(jué)轉(zhuǎn)換和其他有趣的動(dòng)畫(huà)效果。
Transition Animations Support Changes to Layer Visibility - 轉(zhuǎn)換動(dòng)畫(huà)支持更改層可見(jiàn)性
顧名思義,轉(zhuǎn)換動(dòng)畫(huà)對(duì)象為圖層創(chuàng)建動(dòng)畫(huà)視覺(jué)轉(zhuǎn)換。 過(guò)渡對(duì)象最常見(jiàn)的用途是以協(xié)調(diào)的方式動(dòng)畫(huà)化一層的外觀和另一層的消失。 與基于屬性的動(dòng)畫(huà)不同,動(dòng)畫(huà)更改圖層的一個(gè)屬性,轉(zhuǎn)換動(dòng)畫(huà)會(huì)操縱圖層的緩存圖像,以創(chuàng)建通過(guò)單獨(dú)更改屬性難度或不可能執(zhí)行的視覺(jué)效果。 轉(zhuǎn)換的標(biāo)準(zhǔn)類型可讓您執(zhí)行顯示,推送,移動(dòng)或交叉淡入淡出動(dòng)畫(huà)。 在OS X上,您還可以使用Core Image過(guò)濾器創(chuàng)建使用其他類型的效果(如擦除,卷曲,波紋或自定義效果)的轉(zhuǎn)換。
要執(zhí)行轉(zhuǎn)換動(dòng)畫(huà),您將創(chuàng)建一個(gè)CATransition對(duì)象并將其添加到轉(zhuǎn)換中涉及的層。 您可以使用轉(zhuǎn)換對(duì)象來(lái)指定要執(zhí)行的轉(zhuǎn)換類型以及轉(zhuǎn)換動(dòng)畫(huà)的起點(diǎn)和終點(diǎn)。 您也不需要使用整個(gè)轉(zhuǎn)換動(dòng)畫(huà)。 轉(zhuǎn)換對(duì)象允許您指定要在動(dòng)畫(huà)時(shí)使用的開(kāi)始和結(jié)束進(jìn)度值。 這些值可讓您在其中點(diǎn)進(jìn)行動(dòng)畫(huà)開(kāi)始或結(jié)束。
下面代碼顯示了用于在兩個(gè)視圖之間創(chuàng)建動(dòng)畫(huà)推送轉(zhuǎn)換的代碼。 在該示例中,myView1
和myView2
都位于同一父視圖中的相同位置,但只有myView1當(dāng)前可見(jiàn)。 推送過(guò)渡會(huì)導(dǎo)致myView1向左滑動(dòng),并且漸隱,直到它隱藏,而myView2從右側(cè)滑入并變?yōu)榭梢?jiàn)。 更新兩個(gè)視圖的隱藏屬性可確保在動(dòng)畫(huà)結(jié)束時(shí)兩個(gè)視圖的可視性是正確的。
//Animating a transition between two views in iOS
CATransition* transition = [CATransition animation];
transition.startProgress = 0;
transition.endProgress = 1.0;
transition.type = kCATransitionPush;
transition.subtype = kCATransitionFromRight;
transition.duration = 1.0;
// Add the transition animation to both layers
[myView1.layer addAnimation:transition forKey:@"transition"];
[myView2.layer addAnimation:transition forKey:@"transition"];
// Finally, change the visibility of the layers.
myView1.hidden = YES;
myView2.hidden = NO;
當(dāng)同一個(gè)轉(zhuǎn)換中涉及到兩個(gè)層時(shí),可以使用相同的轉(zhuǎn)換對(duì)象。 使用相同的轉(zhuǎn)換對(duì)象也簡(jiǎn)化了必須編寫的代碼。 但是,您可以使用不同的轉(zhuǎn)換對(duì)象,如果每個(gè)層的轉(zhuǎn)換參數(shù)不同,那么肯定需要這樣做。
下面代碼顯示了如何使用Core Image過(guò)濾器在OS X上實(shí)現(xiàn)過(guò)渡效果。在使用所需參數(shù)配置過(guò)濾器后,將其分配給過(guò)渡對(duì)象的過(guò)濾器屬性。 之后,應(yīng)用動(dòng)畫(huà)的過(guò)程與其他類型的動(dòng)畫(huà)對(duì)象相同。
// Using a Core Image filter to animate a transition on OS X
// Create the Core Image filter, setting several key parameters.
CIFilter* aFilter = [CIFilter filterWithName:@"CIBarsSwipeTransition"];
[aFilter setValue:[NSNumber numberWithFloat:3.14] forKey:@"inputAngle"];
[aFilter setValue:[NSNumber numberWithFloat:30.0] forKey:@"inputWidth"];
[aFilter setValue:[NSNumber numberWithFloat:10.0] forKey:@"inputBarOffset"];
// Create the transition object
CATransition* transition = [CATransition animation];
transition.startProgress = 0;
transition.endProgress = 1.0;
transition.filter = aFilter;
transition.duration = 1.0;
[self.imageView2 setHidden:NO];
[self.imageView.layer addAnimation:transition forKey:@"transition"];
[self.imageView2.layer addAnimation:transition forKey:@"transition"];
[self.imageView setHidden:YES];
注意:在動(dòng)畫(huà)中使用Core Image
過(guò)濾器時(shí),最棘手的部分是配置過(guò)濾器。 例如,使用條形滑動(dòng)轉(zhuǎn)換,指定輸入角度太高或太低可能會(huì)使它看起來(lái)好像沒(méi)有轉(zhuǎn)換發(fā)生。 如果您沒(méi)有看到您想要的動(dòng)畫(huà),請(qǐng)嘗試將過(guò)濾器參數(shù)調(diào)整為不同的值,以查看是否更改了結(jié)果。
Customizing the Timing of an Animation - 自定義動(dòng)畫(huà)的時(shí)序
Timing是動(dòng)畫(huà)的重要組成部分,Core Animation
可以通過(guò)CAMediaTiming
協(xié)議的方法和屬性為動(dòng)畫(huà)指定精確的時(shí)間信息。 兩個(gè)核心動(dòng)畫(huà)類采用這個(gè)協(xié)議。 CAAnimation
類采用它,以便您可以在動(dòng)畫(huà)對(duì)象中指定時(shí)間信息。 CALayer還采用它,以便您可以為隱式動(dòng)畫(huà)配置一些與時(shí)序相關(guān)的功能,盡管包含這些動(dòng)畫(huà)的隱式事務(wù)對(duì)象通常提供優(yōu)先級(jí)的默認(rèn)時(shí)序信息。
當(dāng)考慮時(shí)間和動(dòng)畫(huà)時(shí),了解層對(duì)象如何隨著時(shí)間的推移是重要的。 每個(gè)層都有自己的本地時(shí)間,用于管理動(dòng)畫(huà)時(shí)序。 通常,兩個(gè)不同層的本地時(shí)間足夠接近,您可以為每個(gè)層指定相同的時(shí)間值,并且用戶可能不會(huì)注意到任何內(nèi)容。 然而,層的本地時(shí)間可以由其父層或其自己的時(shí)序參數(shù)修改。 例如,更改圖層的速度屬性會(huì)導(dǎo)致該圖層(及其子圖層)的動(dòng)畫(huà)持續(xù)時(shí)間按比例更改。
為了幫助您確定時(shí)間值適用于給定層,CALayer類定義convertTime:fromLayer:和convertTime:toLayer:方法。 您可以使用這些方法將固定時(shí)間值轉(zhuǎn)換為圖層的本地時(shí)間,或?qū)r(shí)間值從一個(gè)層轉(zhuǎn)換為另一層。 這些方法會(huì)考慮可能影響圖層本地時(shí)間的媒體時(shí)序?qū)傩裕⒎祷乜膳c其他圖層一起使用的值。 下面代碼顯示了一個(gè)示例,您應(yīng)該定期使用以獲取圖層的當(dāng)前本地時(shí)間。 CACurrentMediaTime
函數(shù)是一個(gè)方便的函數(shù),它返回計(jì)算機(jī)的當(dāng)前時(shí)鐘時(shí)間,該方法將采用該函數(shù)并將其轉(zhuǎn)換為圖層的本地時(shí)間。
// Getting a layer’s current local time
CFTimeInterval localLayerTime = [myLayer convertTime:CACurrentMediaTime() fromLayer:nil];
一旦您在圖層的本地時(shí)間中有時(shí)間值,您可以使用該值更新動(dòng)畫(huà)對(duì)象或圖層的時(shí)間相關(guān)屬性。 通過(guò)這些時(shí)序?qū)傩裕梢詫?shí)現(xiàn)一些有趣的動(dòng)畫(huà)行為,其中包括:
使用
beginTime
屬性設(shè)置動(dòng)畫(huà)的開(kāi)始時(shí)間。 通常,動(dòng)畫(huà)將在下一個(gè)更新周期開(kāi)始。 您可以使用beginTime參數(shù)將動(dòng)畫(huà)開(kāi)始時(shí)間延遲幾秒鐘。 將兩個(gè)動(dòng)畫(huà)連接在一起的方法是設(shè)置一個(gè)動(dòng)畫(huà)的開(kāi)始時(shí)間,以匹配另一個(gè)動(dòng)畫(huà)的結(jié)束時(shí)間。如果延遲動(dòng)畫(huà)的開(kāi)始,您可能還需要將fillMode屬性設(shè)置為kCAFillModeBackwards
。 即使圖層樹(shù)中的圖層對(duì)象包含不同的值,此填充模式也會(huì)使圖層顯示動(dòng)畫(huà)的起始值。 沒(méi)有這種填充模式,您將看到在動(dòng)畫(huà)開(kāi)始執(zhí)行之前跳轉(zhuǎn)到最終值。 其他填充模式也可用。autoreverses
屬性會(huì)使動(dòng)畫(huà)在指定的持續(xù)時(shí)間內(nèi)執(zhí)行,然后返回到動(dòng)畫(huà)的起始值。 您可以將此屬性與repeatCount
屬性組合,以在起始值和結(jié)束值之間來(lái)回動(dòng)畫(huà)。 將重復(fù)計(jì)數(shù)設(shè)置為自動(dòng)轉(zhuǎn)換動(dòng)畫(huà)的整數(shù)(例如1.0)會(huì)導(dǎo)致動(dòng)畫(huà)停止其起始值。 添加額外的一半步驟(例如重復(fù)計(jì)數(shù)為1.5)會(huì)導(dǎo)致動(dòng)畫(huà)停止其結(jié)束值。使用具有組動(dòng)畫(huà)的
timeOffset
屬性可以在稍后時(shí)間啟動(dòng)一些動(dòng)畫(huà)。
Pausing and Resuming Animations - 停止和暫停動(dòng)畫(huà)
要暫停動(dòng)畫(huà),您可以利用層采用CAMediaTiming
協(xié)議并將層的動(dòng)畫(huà)速度設(shè)置為0.0。 將速度設(shè)置為零可暫停動(dòng)畫(huà),直到將值更改回非零值。 下面代碼顯示了如何暫停和恢復(fù)動(dòng)畫(huà)的簡(jiǎn)單示例。
// Pausing and resuming a layer’s animations
-(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;
}
Explicit Transactions Let You Change Animation Parameters - 顯式轉(zhuǎn)場(chǎng)讓您更改動(dòng)畫(huà)參數(shù)
對(duì)圖層進(jìn)行的每一次更改都必須是事務(wù)的一部分。 CATransaction
類在適當(dāng)?shù)臅r(shí)間管理動(dòng)畫(huà)的創(chuàng)建和分組及其執(zhí)行。 在大多數(shù)情況下,您不需要?jiǎng)?chuàng)建自己的事務(wù)。 每當(dāng)向其中一個(gè)圖層添加顯式或隱式動(dòng)畫(huà)時(shí),Core Animation
將自動(dòng)創(chuàng)建一個(gè)隱式事務(wù)。 但是,您也可以創(chuàng)建顯式的事務(wù)來(lái)更精確地管理這些動(dòng)畫(huà)。
您可以使用CATransaction
類的方法創(chuàng)建和管理事務(wù)。 開(kāi)始(并隱式創(chuàng)建)一個(gè)新的事務(wù)調(diào)用begin
類方法; 要結(jié)束該事務(wù),調(diào)用commit
類方法。 在這些調(diào)用之間是您想成為事務(wù)一部分的更改。 例如,要更改圖層的兩個(gè)屬性,可以使用下面的代碼。
// Creating an explicit transaction
[CATransaction begin];
theLayer.zPosition=200.0;
theLayer.opacity=0.0;
[CATransaction commit];
使用交易的主要原因之一是在顯式事務(wù)的限制內(nèi),您可以更改持續(xù)時(shí)間,計(jì)時(shí)功能和其他參數(shù)。 您還可以為整個(gè)事務(wù)分配完成塊,以便在動(dòng)畫(huà)組完成時(shí)通知您的應(yīng)用。 更改動(dòng)畫(huà)參數(shù)需要使用setValue:forKey:方法修改事務(wù)字典中的相應(yīng)鍵。 例如,要將默認(rèn)持續(xù)時(shí)間更改為10秒,您可以更改kCATransactionAnimationDuration鍵,如下所示。
//Changing the default duration of animations
[CATransaction begin];
[CATransaction setValue:[NSNumber numberWithFloat:10.0f]
forKey:kCATransactionAnimationDuration];
// Perform the animations
[CATransaction commit];
您可以在要為不同動(dòng)畫(huà)集提供不同默認(rèn)值的情況下嵌套事務(wù)。 要將一個(gè)事務(wù)嵌套在另一個(gè)事務(wù)中,只需再次調(diào)用begin類方法。 每個(gè)開(kāi)始調(diào)用必須通過(guò)對(duì)commit方法的相應(yīng)調(diào)用進(jìn)行匹配。 只有在您提交最外層事務(wù)的更改后,Core Animation
才會(huì)啟動(dòng)相關(guān)聯(lián)的動(dòng)畫(huà)。
下面代碼顯示了嵌套在另一個(gè)事務(wù)中的一個(gè)事務(wù)的示例。 在此示例中,內(nèi)部事務(wù)將更改與外部事務(wù)相同的動(dòng)畫(huà)參數(shù),但使用不同的值。
// Nesting explicit transactions
[CATransaction begin]; // Outer transaction
// Change the animation duration to two seconds
[CATransaction setValue:[NSNumber numberWithFloat:2.0f]
forKey:kCATransactionAnimationDuration];
// Move the layer to a new position
theLayer.position = CGPointMake(0.0,0.0);
[CATransaction begin]; // Inner transaction
// Change the animation duration to five seconds
[CATransaction setValue:[NSNumber numberWithFloat:5.0f]
forKey:kCATransactionAnimationDuration];
// Change the zPosition and opacity
theLayer.zPosition=200.0;
theLayer.opacity=0.0;
[CATransaction commit]; // Inner transaction
[CATransaction commit]; // Outer transaction
Adding Perspective to Your Animations - 為您的動(dòng)畫(huà)添加視覺(jué)
應(yīng)用程序可以在三個(gè)空間維度中操縱圖層,但為了簡(jiǎn)單起見(jiàn),Core Animation
使用平行投影顯示圖層,這將基本上將場(chǎng)景平坦化為二維平面。 這種默認(rèn)行為導(dǎo)致具有不同zPosition
值的相同大小的圖層顯示為相同的大小,即使它們?cè)趜軸上相距較遠(yuǎn)。 你通常會(huì)在三維中觀看這樣一個(gè)視角的觀點(diǎn)已經(jīng)消失了。 但是,您可以通過(guò)修改圖層的轉(zhuǎn)換矩陣來(lái)包含透視信息來(lái)更改該行為。
當(dāng)修改場(chǎng)景的透視圖時(shí),需要修改包含正在查看的圖層的超層的sublayerTransform
矩陣。 修改超層通過(guò)將相同的透視信息應(yīng)用于所有子層來(lái)簡(jiǎn)化您必須編寫的代碼。 它還確保透視圖正確應(yīng)用于在不同平面中彼此重疊的兄弟子層。
下面代碼顯示了為父層創(chuàng)建簡(jiǎn)單透視變換的方法。 在這種情況下,自定義eyePosition
變量指定沿著Z軸的相對(duì)距離以查看圖層。 通常你為eyePosition指定一個(gè)正值,以便按照預(yù)期的方式保持這些層面。 更大的值導(dǎo)致更平坦的場(chǎng)景,而較小的值會(huì)導(dǎo)致層之間更顯著的視覺(jué)差異。
// Adding a perspective transform to a parent layer
CATransform3D perspective = CATransform3DIdentity;
perspective.m34 = -1.0/eyePosition;
// Apply the transform to a parent layer.
myParentLayer.sublayerTransform = perspective;
配置父層后,您可以更改任何子層的zPosition
屬性,并根據(jù)其距離眼睛位置的相對(duì)距離觀察其大小如何變化。
后記
未完,待續(xù)~~~