1. CoreAnimation 在不需要使用OpenGL或OPenGL ES框架的前提下就可以很容易創(chuàng)建高性能, 基于GPU的動(dòng)畫效果. CoreAnimation框架提供的有硬件加速視頻渲染效果. 從高層次角度看, Core Animation包含兩類對(duì)象: Layers和Animations.
2. Layers 圖層對(duì)象有CALayer類定義, 并用于管理屏幕中可視化內(nèi)容的元素. 這里所說的內(nèi)容一般都是圖片或Bezier路徑, 不過圖層本身具有可被設(shè)置的可視化特征. 比如他的顏色, 透明度和角半徑. 除了CALayer框架還定義了很多實(shí)用的子類, 比如用于渲染貼圖內(nèi)容的CATextLayer類和用于渲染Beizier路徑的CAShapeLyer類. 這兩個(gè)類在創(chuàng)建動(dòng)畫疊加效果時(shí)都非常重要.
3. Animations 動(dòng)畫對(duì)象是抽象類CAAnimation的實(shí)例, 定義所有動(dòng)畫類型共有的一些核心動(dòng)畫行為.該框架定義了CAAnimation的許多具體子類. 最常用的就是CABasicAnimation和CAKeyFrameAniation. 這些類將動(dòng)畫狀態(tài)變?yōu)閱为?dú)的圖層屬性, 以便創(chuàng)建簡(jiǎn)單的和復(fù)雜的動(dòng)畫效果. CABasicAnimation 可以讓你創(chuàng)建簡(jiǎn)單的單關(guān)鍵幀動(dòng)畫, 意味著在一段時(shí)間內(nèi)將屬性狀態(tài)以動(dòng)畫方式由一種狀態(tài)變?yōu)榱硪环N狀態(tài). 這個(gè)類實(shí)現(xiàn)簡(jiǎn)單動(dòng)畫時(shí)非常實(shí)用. 比如動(dòng)態(tài)調(diào)整圖層的尺寸, 位置和背景色. CAKeyFrameAnimation用于實(shí)現(xiàn)更高級(jí)的功能, 它對(duì)動(dòng)畫中的關(guān)鍵幀有著更多的控制. 比如當(dāng)一個(gè)圖層沿著Bezier路徑動(dòng)態(tài)顯示, 可以用到關(guān)鍵幀動(dòng)畫來指定具體的時(shí)間和節(jié)奏.
CALayer *parentLayer = //parent layer
UIImage *image = [UIImage imageNamed:@"image.png"];
CALayer *imageLayer = [CALayer layer];
imageLayer.contents = (id)image.CGImage
imageLayer.contentScale = [UIScreen mainScreen].scale;
CGFloat midX = CGRectGetMidX(parentLayer.bounds);
CGFloat midY = CGRectGEtMidY(parentLayer.bounds);
imageLayer.bounds = CGRectMake(0, 0, image.size.width, image.size.height);
imageLayer.position = CGPointMake(midX, midY);
[parentLayer addSublayer:imageLayer];
CABasicAnimation *rotationAnimation = [CABasicAnimation animationWithKeyPath:@"transform.rotation.z"];
rotationAnimation.toValue = @(2 * M_PI);
roationAnimation.duration = 3.0f;
rotationAnimation.repeatCount = HUGE_VALF;
[imageLayer addAnimation:rotationAnimation forKey:@"rotateAnimation"];
4. 在AVFoundation中使用Core Animation, 使用Core Animation為視頻應(yīng)用程序創(chuàng)建疊加效果的方法同使用它在iOS和OS X平臺(tái)創(chuàng)建實(shí)時(shí)動(dòng)畫效果的方法幾乎一樣. 最大的區(qū)別在于運(yùn)行動(dòng)畫的時(shí)間模型. 當(dāng)創(chuàng)建實(shí)時(shí)動(dòng)畫時(shí), CAAnimation實(shí)例從系統(tǒng)主機(jī)獲取執(zhí)行時(shí)間.
5. AVSynchronizedLayer播放, AVFoundation提供了一個(gè)專門的CALayer的子類AVSynchronizedLayer, 用于與給定的AVPlayerItem實(shí)例同步時(shí)間, 這個(gè)圖層本身不展示任何內(nèi)容. 僅用來與圖層子樹協(xié)同時(shí)間. 這樣所在的繼承關(guān)系中附屬于改圖層的動(dòng)畫都可以從激活的AVPlayerItem實(shí)例中獲取相應(yīng)的執(zhí)行時(shí)間. 通常使用AVSynchronizedLayer時(shí)會(huì)將其整合到播放器視圖的圖層繼承關(guān)系中. 同步圖層直接呈現(xiàn)在視頻圖層之上. 這樣就可以添加動(dòng)畫標(biāo)題, 水印或下沿字幕到播放器視頻中, 并與播放器欄行為保持完美同步.
6. 使用AVVideoCompositionCoreAnimationTool導(dǎo)出, 要將Core Animation圖層和動(dòng)畫整合到導(dǎo)出視頻中, 需要使用AVVideoCompositionCoreAnimationTool類, AVVideoComposition使用這個(gè)類將Core Animation效果作為視頻組合后期處理階段納入.
7. Core Animation框架的默認(rèn)行為是執(zhí)行動(dòng)畫并在動(dòng)畫行為完成后進(jìn)行處理, 通常這些行為就是我們希望在按理中使用的, 因?yàn)闀r(shí)間一旦過去就沒法返回了. 不過對(duì)于視頻動(dòng)畫就會(huì)有問題. 所以需要設(shè)置動(dòng)畫的removedOnCompletion屬性來NO來禁用這一行為. 如果不這樣做, 則動(dòng)畫效果是一次性的. 如果用戶重新播放視頻或在時(shí)間軸上移動(dòng)戳插條也不會(huì)再次看到動(dòng)畫. 動(dòng)畫的beginTime屬性被設(shè)置為0.0的話是不會(huì)看到動(dòng)畫效果的. Core Animation將值為0.0的beginTime對(duì)象轉(zhuǎn)換為CACurrentMediaTime(), 這是當(dāng)前主機(jī)時(shí)間, 同影片時(shí)間軸中的有效時(shí)間沒有關(guān)系. 如果希望在影片開頭加入動(dòng)畫, 將動(dòng)畫的beginTime屬性設(shè)置成AVCoreAnimationBeginTimeAtZero常量.
8. 添加動(dòng)畫標(biāo)題, 在Core Animation中使用AVComposition的一個(gè)挑戰(zhàn)就是協(xié)調(diào)不同的概念和時(shí)間模型. 在使用AVComposition時(shí), 考慮的是軌道以及CMTime和CMTimeRange值, Core Animation沒有軌道的概念并使用浮點(diǎn)型數(shù)值來表示時(shí)間. 在一個(gè)簡(jiǎn)單場(chǎng)景中我們可以使用Core Animation自己的概念, 不過當(dāng)需要?jiǎng)?chuàng)建一個(gè)更復(fù)雜的案例時(shí), 最好在兩個(gè)框架之間定義一個(gè)通用的抽象概念來使創(chuàng)建組合資源和動(dòng)畫時(shí)具有標(biāo)準(zhǔn)化的模型.?
9. 創(chuàng)建一個(gè)簡(jiǎn)單的THTimeLineItem對(duì)象THTittleItem, 用于將動(dòng)畫標(biāo)題添加到項(xiàng)目中.
@interface THTimtleItem: THTimelineItem
+ (instancetype)titleItemWithText:(NSString *)text image:(UIImage *)image;
- (instancetype)initWithText:(NSString *)text image:(UIImage *)image;
@property (copy, nonatomic) NSString *identifier;
@property (nonatomic) BOOL animateImage;
@property (nonatomic) BOOL useLargeFont;
- (CALayer *)buildLayer;
@end
@interface THTitleItem ()
@property (nonatomic, copy ) NSString *text;
@property (nonatomic, strong) UIImage *image;
@property (nonatomic) CGRect bounds;
@end
@implementation THTitleItem?
+ (instancetype)titleItemWithText:(NSString *)text image:(UIImage *)image {
? ? return [[self alloc] initWithText:text image:image];
}
- (instancetype)initWithText:(NSString *)text image:(UIImage *)image {
? ? self = [super init];
? ? if (self) {
? ? ? ? _text = text;
? ? ? ? _image = image;
? ? ? ? _bounds = TH720pVideoRect;
????}
? ? return self;
}
- (CALayer *)buildLayer {
? ? CALayer *parentLayer = [CALayer layer];
? ? parentLayer.frame = self.bounds;
? ? parentLayer.opacity = 0.0f;
? ? CALayer *imageLayer = [self makeImageLayer];
? ? [parentLayer addSubLayer:imageLayer];
? ? CALayer *textLayer = [self makeTextLayer];
? ? [parentLayer addSublayer:textLayer];
? ? return parentLayer;
}
- (CALayer *)makeImageLayer {
? ? CGSize imageSize = self.image.size;
? ? CALayer *layer = [CALayer layer];
? ? layer.contents = (id)self.image.CGImage;
? ? layer.allowsEdgeAntialiasing = YES;
? ? layer.bounds = CGRectMake(0, 0, imageSize.width, imageSize.height);
? ? layer.position = CGPointMake(CGRectGetMidX(self.bounds) - 20, 270);
? ? return layer;
}
- (CALayer *)makeTextLayer {
? ? CGFloat fontSize = self.userLargeFont ? 64 : 54;
? ? UIFont *font = [UIFont fontWithName:@"GillSans-Bold" size:fontSize];
? ? NSDictionary *attrs = @{NSFontAttributeName:font, NSForegroundColorAttributeName: (id)[UIColor whiteColor].CGColor};
? ? NSAttributeString *string = [[NSAttributedString alloc] initWithString:self.text attributes:attrs];
? ? CGSize textSize = [self.text sizeWithAttributes:attrs];
? ? CATextLayer *layer = [CATextLayer layer]; ? ?
? ? layer.string = string;
? ? layer.bounds = CGRectMake(0, 0, textSize.width, textSize.height);
? ? layer.position = CGPointMake(CGRectGetMidX(self.bounds), 470.0);
? ? layer.backgroundColor = [UIColor clearColor].CGColor;
? ? return layer;
}
@end
10. 創(chuàng)建淡入淡出動(dòng)畫效果
@implementation THTitleItme
...
- (CALayer *)buildLayer {
? ? CALayer *parentLayer = [CALayer layer];
? ? parentLayer.frame = self.bounds;
? ? parentLayer.opacity = 0.0f;
? ? CALayer *imageLayer = [self makeImageLayer];
? ? [parentLayer addSubLayer:imageLayer];
? ? CALayer *textLayer = [self makeTextLayer];
? ? [parentLayer addSublayer:textLayer];
? ? // --- build and attach animations ---
? ? CAAnimation *fadeInFadeOutAnimation = [self makeFadeInFadeOutAnimation];
? ? [parentLayer addAnimation:fadeInFadeOutAnimation forKey:nil];
? ? return parentLayer;
}
- (void)makeFadeInFadeOutAnimation {
? ? CAKeyframeAnimation *animation = [CAKeyAnimation animationWithKeyPath:@"opacity"];
? ? animation.values = @[@0, @1, @1, @0];
? ? animation.keyTimes = @[@0, @0.2, @0.8, @1];
? ? animation.removeOnCompletion = NO;
? ? return animation;
}
@end
11. 為標(biāo)題圖片添加動(dòng)畫
- (CALayer *)buildLayer {
? ? CALayer *parentLayer = [CALayer layer];
? ? parentLayer.frame = self.bounds;
? ? parentLayer.opacity = 0;
? ? CALayer *imageLayer = [self makeImageLayer];
????[parentLayer addSubLayer:imageLayer];
? ? CALayer *textLayer = [self makeTextLayer];
? ? [parentLayer addSubLayer:textLayer];
? ? CAAnimation *fadeInFadeOutAnimation = [self makeFadeInFadeOutAnimation];
? ? [parentLayer addAnimation:fadeInFadeOutAnimation forKey:nil];
? ? if (self.animateImage) {
? ????? //應(yīng)用一個(gè)3d繞y軸旋轉(zhuǎn)動(dòng)畫, 必須設(shè)置父視圖的透視變化
? ? ? ? parentLayer.sublayerTransform = THMakePerspectiveTransform(1000);
? ? ? ? CAAnimation *spinAnimation = [self make3DSpinAnimation]; ? ? ?
? ? ? ? NSTimeInterval offset = spinAnimation.beginTime + spinAnimation.duration - 0.5;
? ? ? ? CAAnimation *popAnimation = [self makePopAnimationWithTimingOffset:offset];
? ? [imageLayer addAnimation:spinAnimation forKey:nil];?
? ? [imageLayer addAnimation:popAnimation forKey:nil];
????}
? ? return parentLayer; ? ?
}
static CATransform3D THMakePerspectiveTransform(CGFloat eyePosition) {
? ? CATransform3d transform = CGTransform3DIdentify;
? ? transform.m34 = -1.0 / eyePosition;
? ? return transform;
}
- (CAAnimation *)make3DSpinAnimation {
? ? CABasicAnimation *animation = [CABasicAnimation?animationWithKeyPath@"transform.rotation.y"];
????animation.toValue = @{(4 * M_PI) * -1};? ??
? ? animation.beginTime = CMTimeGetSeconds(self.startTimeInTimeline) + 0.2;
? ? animation.duration = CGTimeGetSeconds(self.timeRange.duratoin) * 0.4;
? ? animation.removeOnCompletion = NO;
? ? animation.timingFunction = [CAMediaTimingFunction functionWithName:kCAMediaTimingFunctionEaseInEaseOut];
? ? return animation;
}
- (CAAnimation *)makePopAnimationWithTimingOffset:(NSTimeInterval)offset {
? ? CABasicAnimation *animation = [CABasicAnimation animationWithKeyPath:@"transform.scale"];
? ? animation.toValue = @1.3;
? ? animation.beginTime = offset;
? ? animation.duration = 0.35;
? ? animation.autoreverses = YES; ? ?
? ? animation.removeOnCompletion = NO;
? ? animation.timingFunction = [CADediaTimingFunction functionWithName:kCAMediaTimingFunctionEaseOut];
? ? return animation;
}