AVFoundation开发秘籍笔记-12动画图层内容CoreAnimation

一、Core Animation

提供一种简单的、声明式程序模型使得在不需要使用OpenGL或者OpenGL ES框架的情况下就可以很容易地创建高性能、基于GPU的动画效果。

Core Animation包含两类对象:

  • Layers。图层对象由CALayer类定义,并用于管理屏幕内可视化内容的元素。这里所说的内容一般都是图片或者Bezier路径,不过图层本身具有可被设置的可视化特征,比如背景色、透明度和角半径。图层定义了自身的几何属性,bounds和position,可以将这些原色组合到图层的层级结构中用于创建更复杂的界面。

    • CATextLayer:用于渲染贴图内容。
    • CAShaperLayer:用来渲染Bezier路径。
  • Animations。动画对象是抽象类CAAnimation的实例,定义所有动画类型所共有的一些核心动画行为。该框架定义了CAAnimation的许多具体子类,最常用的就是CABasicAnimation和CAKeyFrameAnimation。将动画状态变为单独的图层熟悉感,一边创建简单和复杂的动画效果。

    • CABasicAnimation:创建简单的单关键帧动画,意味着在一段时间内将属性状态以动画方式由一种状态变为另一种状态。比如动态调整图层尺寸、位置、背景色。
    • CAKeyFrameAnimation:实现更高级功能,对动画中的关键帧有更多的控制。

创建一个绕z轴旋转的动画

CALayer *parentLayer = self.view.layer;
    
UIImage *image = [UIImage imageNamed:@"lavf_cover"];
CALayer *imageLayer = [CALayer layer];
    
// 设置layer内容
imageLayer.contents = (id)image.CGImage;
imageLayer.contentsScale = [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];
    
    
//z轴旋转动画
CABasicAnimation *rotationAnimation = [CABasicAnimation animationWithKeyPath:@"transform.rotation.z"];
rotationAnimation.toValue = @(2 * M_PI);
rotationAnimation.duration = 3.f;
rotationAnimation.repeatCount = HUGE_VAL;
    
[imageLayer addAnimation:rotationAnimation forKey:@"r"];

二、在AVFoundation中使用Core Animation

AVFoundation使用CoreAnimation,动画需要紧密的与视频时间轴绑定,不能在根据主机时间确定时间,这是两种不同的执行时间模式。

1、AVSynchronizedLayer

AVSynchronizedLayer用于跟定定的AVPlayerItem实例同步时间。这个图层本身不展示任何内容,仅用来与图层子树协同时间。

在继承关系中附属于该图层的动画都可以从激活的AVPlayerItem实例中获取相应的执行时间。

通常使用AVSynchronizedLayer会将其整合到播放器视图的图层继承关系中,同步图层直接呈现在视图图层之上。

不只是这一个用法,还有其他用法。

2、使用AVVideoCompositionCoreAnimationTool导出

将CoreAnimation图层和动画整合到导出视频中,需要使用AVVideoCompositionAnimationTool类。AVVideoComposition使用这个类将Core Animation效果作为食品组合的后期处理阶段纳入。

它可以将组合视频帧至于视频图层中,并在之后渲染包含自定义底价效果的动画图层来生成最终的视频帧。

问题:

  • CoreAnimation框架的默认行为是执行动画并在动画行为完成之后进行处理。通常这一行为就是我们希望在实时案例中使用,因为时间一旦过去就没法返回。对于视频动画需要设置动画的removedOnCompletion属性为NO来禁用这一行为。如果不设置,动画效果就是一次性的,如果用户重新播放视频或在时间轴上移动进度条也不会再次看到动画。
  • 动画的时间,beginTime设置为0.0不会看到动画效果。如果希望在影片开头加入动画,将动画的beginTime设置为AVCoreAnimationBeginTimeAtZero常量。

三、添加动画标题

在CoreAnimation中使用AVComposition的一个挑战就是协调不同的概念和时间模型。在使用AVComposition时,考虑的是轨道以及CMTime和CMTimeRange值。

CoreAnimation没有轨道的概念并使用浮点数来表示时间。

简单场景中可以使用CoreAnimation自己的概念,不过当需要创建一个复杂案例时,最好在两个框架之间定义一个通用的抽象概念来创建组合资源和动画时具有标准化的模型。

创建动画图层:

#import "THTitleItem.h"
#import "THConstants.h"

@interface THTitleItem ()
@property (copy, nonatomic) NSString *text;
@property (strong, nonatomic) 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) {

        // Listing 12.2
        
        _text = text;
        _image = image;
        _bounds = TH720pVideoRect;
        //

    }
    return self;
}

- (CALayer *)buildLayer {

    // 创建一个曾对象,父图层,包含相应的图片、文本图层和尺寸
    CALayer *parentLayer = [CALayer layer];
    parentLayer.frame = self.bounds;
    //设置 opacity为0,不可见,只有动态调整属性值控制可见性
    parentLayer.opacity = 0.0f;
    
    CALayer *imageLayer = [self makeImageLayer];
    [parentLayer addSublayer:imageLayer];
    
    CALayer *textLayer = [self makeTextLayer];
    [parentLayer addSublayer:textLayer];
    
    
    //创建动画
    CAAnimation *fadeInFadeOutAnimation = [self makeFadeInFadeOutAnimation];
    [parentLayer addAnimation:fadeInFadeOutAnimation forKey:@"fadeAnimation"];
    
    if (self.animateImage) {
        //应用一个3D动画效果绕y轴旋转。要实现这个效果需要使用父图层的sublayerTransform属性设置一个透视变化
        
        parentLayer.sublayerTransform = THMakePerspectiveTransform(1000);
        //创建一个CAAnimation来应用旋转动画并计算一个偏移量,用于创建弹出动画。
        CAAnimation *spinAnimation = [self make3DSpinAnimation];
        //之后旋转动画的beginTime和duration,减去半秒,就在旋转动作最后半秒加入一个弹出动画。
        NSTimeInterval offset = spinAnimation.beginTime + spinAnimation.duration -0.5f;
        CAAnimation *popAnimation = [self makePopAnimationWithTimingOffset:offset];
        
        [imageLayer addAnimation:spinAnimation forKey:nil];
        [imageLayer addAnimation:popAnimation forKey:nil];
    }

    return parentLayer;
}

- (CALayer *)makeImageLayer {
    
    // 创建图片层
    
    CGSize imageSize = self.image.size;
    
    CALayer *layer = [CALayer layer];
    // CoreAnimation只能处理CoreGraphics类型,获取UIImage对象的基CGimageRef
    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.0f, 270.0f);

    return layer;
}

- (CALayer *)makeTextLayer {
    
    CGFloat fontSize = self.useLargeFont ? 64:54;
    UIFont *font = [UIFont fontWithName:@"GillSans-Bold" size:fontSize];
    
    NSDictionary *attrs = @{
                            NSFontAttributeName:font,
                            NSForegroundColorAttributeName:(id)[UIColor whiteColor].CGColor
                            };
    NSAttributedString *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);
    layer.backgroundColor = [UIColor clearColor].CGColor;
    return layer;

    return nil;
}

- (CAAnimation *)makeFadeInFadeOutAnimation {
    
    CAKeyframeAnimation *animation = [CAKeyframeAnimation animationWithKeyPath:@"opacity"];
    animation.values = @[@0.0f,@1.0f,@1.0f,@0.0f];
    animation.keyTimes = @[@0.0f,@0.2f,@0.8f,@1.0f];
    
    // 根据视频时间确定动画时间。
    animation.beginTime = CMTimeGetSeconds(self.startTimeInTimeline);
    animation.duration = CMTimeGetSeconds(self.timeRange.duration);
    
    // 设置为NO,动画就不会在执行之后被移除。
    animation.removedOnCompletion = NO;
    return animation;
    
}

- (CAAnimation *)make3DSpinAnimation {

    // 为图片变形添加动画效果,绕y轴旋转
    CABasicAnimation *animation = [CABasicAnimation animationWithKeyPath:@"transform.rotation.y"];
    
    // 图片逆时针方向绕y轴旋转两圈。
    animation.toValue = @((4*M_PI) * -1);
    // 淡入淡出的方式计算beginTime和duration
    animation.beginTime = CMTimeGetSeconds(self.startTimeInTimeline) + 0.2;
    animation.duration = CMTimeGetSeconds(self.timeRange.duration) * 0.4;
    
    // 为动画应用一个时间函数,默认情况下动画会使用线性曲线,略显僵硬
    // 使用缓入缓出曲线。
    animation.timingFunction = [CAMediaTimingFunction functionWithName:kCAMediaTimingFunctionEaseInEaseOut];
    return animation;
    
}

- (CAAnimation *)makePopAnimationWithTimingOffset:(NSTimeInterval)offset {
    
    // 弹出动画,做一个缩放效果。
    CABasicAnimation *animation = [CABasicAnimation animationWithKeyPath:@"transform.scale"];
    animation.toValue = @1.3f;
    //作为前一个动画的一部分,设置beginTime为传递给方法的偏移量。
    animation.beginTime = offset;
    animation.duration = 0.3f;
    // 动画对象的自动回溯,可以反向运行动画,使他返回到初始状态。
    animation.autoreverses = YES;
    animation.removedOnCompletion = NO;
    
    animation.timingFunction = [CAMediaTimingFunction functionWithName:kCAMediaTimingFunctionEaseInEaseOut];
    return animation;
}

#pragma clang diagnostic push
#pragma clang diagnostic ignored "-Wunused"

static CATransform3D THMakePerspectiveTransform(CGFloat eyePosition) {
    CATransform3D transform = CATransform3DIdentity;
    transform.m34 = -1.0 / eyePosition;
    return transform;
}

#pragma clang diagnostic pop

@end
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 203,271评论 5 476
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 85,275评论 2 380
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 150,151评论 0 336
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,550评论 1 273
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,553评论 5 365
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,559评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,924评论 3 395
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,580评论 0 257
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,826评论 1 297
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,578评论 2 320
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,661评论 1 329
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,363评论 4 318
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,940评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,926评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,156评论 1 259
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 42,872评论 2 349
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,391评论 2 342

推荐阅读更多精彩内容