1. CoreAnimation 在不需要使用OpenGL或OPenGL ES框架的前提下就可以很容易创建高性能, 基于GPU的动画效果. CoreAnimation框架提供的有硬件加速视频渲染效果. 从高层次角度看, Core Animation包含两类对象: Layers和Animations.
2. Layers 图层对象有CALayer类定义, 并用于管理屏幕中可视化内容的元素. 这里所说的内容一般都是图片或Bezier路径, 不过图层本身具有可被设置的可视化特征. 比如他的颜色, 透明度和角半径. 除了CALayer框架还定义了很多实用的子类, 比如用于渲染贴图内容的CATextLayer类和用于渲染Beizier路径的CAShapeLyer类. 这两个类在创建动画叠加效果时都非常重要.
3. Animations 动画对象是抽象类CAAnimation的实例, 定义所有动画类型共有的一些核心动画行为.该框架定义了CAAnimation的许多具体子类. 最常用的就是CABasicAnimation和CAKeyFrameAniation. 这些类将动画状态变为单独的图层属性, 以便创建简单的和复杂的动画效果. CABasicAnimation 可以让你创建简单的单关键帧动画, 意味着在一段时间内将属性状态以动画方式由一种状态变为另一种状态. 这个类实现简单动画时非常实用. 比如动态调整图层的尺寸, 位置和背景色. CAKeyFrameAnimation用于实现更高级的功能, 它对动画中的关键帧有着更多的控制. 比如当一个图层沿着Bezier路径动态显示, 可以用到关键帧动画来指定具体的时间和节奏.
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为视频应用程序创建叠加效果的方法同使用它在iOS和OS X平台创建实时动画效果的方法几乎一样. 最大的区别在于运行动画的时间模型. 当创建实时动画时, CAAnimation实例从系统主机获取执行时间.
5. AVSynchronizedLayer播放, AVFoundation提供了一个专门的CALayer的子类AVSynchronizedLayer, 用于与给定的AVPlayerItem实例同步时间, 这个图层本身不展示任何内容. 仅用来与图层子树协同时间. 这样所在的继承关系中附属于改图层的动画都可以从激活的AVPlayerItem实例中获取相应的执行时间. 通常使用AVSynchronizedLayer时会将其整合到播放器视图的图层继承关系中. 同步图层直接呈现在视频图层之上. 这样就可以添加动画标题, 水印或下沿字幕到播放器视频中, 并与播放器栏行为保持完美同步.
6. 使用AVVideoCompositionCoreAnimationTool导出, 要将Core Animation图层和动画整合到导出视频中, 需要使用AVVideoCompositionCoreAnimationTool类, AVVideoComposition使用这个类将Core Animation效果作为视频组合后期处理阶段纳入.
7. Core Animation框架的默认行为是执行动画并在动画行为完成后进行处理, 通常这些行为就是我们希望在按理中使用的, 因为时间一旦过去就没法返回了. 不过对于视频动画就会有问题. 所以需要设置动画的removedOnCompletion属性来NO来禁用这一行为. 如果不这样做, 则动画效果是一次性的. 如果用户重新播放视频或在时间轴上移动戳插条也不会再次看到动画. 动画的beginTime属性被设置为0.0的话是不会看到动画效果的. Core Animation将值为0.0的beginTime对象转换为CACurrentMediaTime(), 这是当前主机时间, 同影片时间轴中的有效时间没有关系. 如果希望在影片开头加入动画, 将动画的beginTime属性设置成AVCoreAnimationBeginTimeAtZero常量.
8. 添加动画标题, 在Core Animation中使用AVComposition的一个挑战就是协调不同的概念和时间模型. 在使用AVComposition时, 考虑的是轨道以及CMTime和CMTimeRange值, Core Animation没有轨道的概念并使用浮点型数值来表示时间. 在一个简单场景中我们可以使用Core Animation自己的概念, 不过当需要创建一个更复杂的案例时, 最好在两个框架之间定义一个通用的抽象概念来使创建组合资源和动画时具有标准化的模型.
9. 创建一个简单的THTimeLineItem对象THTittleItem, 用于将动画标题添加到项目中.
@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. 创建淡入淡出动画效果
@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. 为标题图片添加动画
- (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) {
//应用一个3d绕y轴旋转动画, 必须设置父视图的透视变化
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;
}