1. CoreAnimation 在不需要使用OpenGL或OPenGL ES框架的前提下就可以很容易創(chuàng)建高性能, 基于GPU的動畫效果. CoreAnimation框架提供的有硬件加速視頻渲染效果. 從高層次角度看, Core Animation包含兩類對象: Layers和Animations.
2. Layers 圖層對象有CALayer類定義, 并用于管理屏幕中可視化內(nèi)容的元素. 這里所說的內(nèi)容一般都是圖片或Bezier路徑, 不過圖層本身具有可被設(shè)置的可視化特征. 比如他的顏色, 透明度和角半徑. 除了CALayer框架還定義了很多實(shí)用的子類, 比如用于渲染貼圖內(nèi)容的CATextLayer類和用于渲染Beizier路徑的CAShapeLyer類. 這兩個類在創(chuàng)建動畫疊加效果時都非常重要.
3. Animations 動畫對象是抽象類CAAnimation的實(shí)例, 定義所有動畫類型共有的一些核心動畫行為.該框架定義了CAAnimation的許多具體子類. 最常用的就是CABasicAnimation和CAKeyFrameAniation. 這些類將動畫狀態(tài)變?yōu)閱为?dú)的圖層屬性, 以便創(chuàng)建簡單的和復(fù)雜的動畫效果. CABasicAnimation 可以讓你創(chuàng)建簡單的單關(guān)鍵幀動畫, 意味著在一段時間內(nèi)將屬性狀態(tài)以動畫方式由一種狀態(tài)變?yōu)榱硪环N狀態(tài). 這個類實(shí)現(xiàn)簡單動畫時非常實(shí)用. 比如動態(tài)調(diào)整圖層的尺寸, 位置和背景色. CAKeyFrameAnimation用于實(shí)現(xiàn)更高級的功能, 它對動畫中的關(guān)鍵幀有著更多的控制. 比如當(dāng)一個圖層沿著Bezier路徑動態(tài)顯示, 可以用到關(guān)鍵幀動畫來指定具體的時間和節(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平臺創(chuàng)建實(shí)時動畫效果的方法幾乎一樣. 最大的區(qū)別在于運(yùn)行動畫的時間模型. 當(dāng)創(chuàng)建實(shí)時動畫時, CAAnimation實(shí)例從系統(tǒng)主機(jī)獲取執(zhí)行時間.
5. AVSynchronizedLayer播放, AVFoundation提供了一個專門的CALayer的子類AVSynchronizedLayer, 用于與給定的AVPlayerItem實(shí)例同步時間, 這個圖層本身不展示任何內(nèi)容. 僅用來與圖層子樹協(xié)同時間. 這樣所在的繼承關(guān)系中附屬于改圖層的動畫都可以從激活的AVPlayerItem實(shí)例中獲取相應(yīng)的執(zhí)行時間. 通常使用AVSynchronizedLayer時會將其整合到播放器視圖的圖層繼承關(guān)系中. 同步圖層直接呈現(xiàn)在視頻圖層之上. 這樣就可以添加動畫標(biāo)題, 水印或下沿字幕到播放器視頻中, 并與播放器欄行為保持完美同步.
6. 使用AVVideoCompositionCoreAnimationTool導(dǎo)出, 要將Core Animation圖層和動畫整合到導(dǎo)出視頻中, 需要使用AVVideoCompositionCoreAnimationTool類, AVVideoComposition使用這個類將Core Animation效果作為視頻組合后期處理階段納入.
7. Core Animation框架的默認(rèn)行為是執(zhí)行動畫并在動畫行為完成后進(jìn)行處理, 通常這些行為就是我們希望在按理中使用的, 因?yàn)闀r間一旦過去就沒法返回了. 不過對于視頻動畫就會有問題. 所以需要設(shè)置動畫的removedOnCompletion屬性來NO來禁用這一行為. 如果不這樣做, 則動畫效果是一次性的. 如果用戶重新播放視頻或在時間軸上移動戳插條也不會再次看到動畫. 動畫的beginTime屬性被設(shè)置為0.0的話是不會看到動畫效果的. Core Animation將值為0.0的beginTime對象轉(zhuǎn)換為CACurrentMediaTime(), 這是當(dāng)前主機(jī)時間, 同影片時間軸中的有效時間沒有關(guān)系. 如果希望在影片開頭加入動畫, 將動畫的beginTime屬性設(shè)置成AVCoreAnimationBeginTimeAtZero常量.
8. 添加動畫標(biāo)題, 在Core Animation中使用AVComposition的一個挑戰(zhàn)就是協(xié)調(diào)不同的概念和時間模型. 在使用AVComposition時, 考慮的是軌道以及CMTime和CMTimeRange值, Core Animation沒有軌道的概念并使用浮點(diǎn)型數(shù)值來表示時間. 在一個簡單場景中我們可以使用Core Animation自己的概念, 不過當(dāng)需要創(chuàng)建一個更復(fù)雜的案例時, 最好在兩個框架之間定義一個通用的抽象概念來使創(chuàng)建組合資源和動畫時具有標(biāo)準(zhǔn)化的模型.?
9. 創(chuàng)建一個簡單的THTimeLineItem對象THTittleItem, 用于將動畫標(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)建淡入淡出動畫效果
@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)題圖片添加動畫
- (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)用一個3d繞y軸旋轉(zhuǎn)動畫, 必須設(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;
}