CAMediaTiming(动画协议)

1、动画协议介绍

有一种通过CAAnimation实现的协议叫做CAMediaTiming,也就是CABasicAnimation和CAKeyframeAnimation的基类(指CAAnimation)。像duration,beginTime和repeatCount这些时间相关的属性都在这个类中。大体而言,协议中定义了8个属性,这些属性通过一些方式结合在一起,准确的控制着时间。

2、duration

为了显示相关属性的不同时间,无论是他们自己还是混合状态,我都会动态的将橙色变为蓝色。下面的块状显示了从开始到结束的动画过程,时间线上每一个标志代表一秒钟。你可以看到时间线上的任意一点,当前颜色即表示动画中的当前时间。比如,duration像下面一样可视。
duration设置为1.5秒,所以动画全程花费了1.5秒变为蓝色。

Snip20161104_1.png

一旦动画完成后,CAAnimation默认从layer上移除。这从上面的图形中也表现出来

3、beginTime

  一旦动画达到最终值,它会从layer上移除。如果layer的颜色是橙色(开始的颜色),那么颜色又会便会橙色。在这个可视化界面中,layer的颜色是白色,所以你也可以看到动画加入layer后的两秒钟又会变白,因为那是动画已经结束了。

我们也形象的描述一下动画的beginTime,这样让人更容易理解。

Snip20161104_2.png

durations设置为1.5秒了,开始时间设为当前时间。(CACurrentMediaTime())加一秒,所以动画在2.5s后结束。动画加入到layer上之后,花费一秒钟时间来启动并呈现出来。结果是1+1.5=2.5

为了让动画从fromValue开始显示,你可以将动画设置为fill backwards。我们可以通过设置fillMode为kCAFillModeBackwards。

Snip20161104_4.png

4、autoreverse

autoreverses属性可以产生从初始值到最终值,并反过来回到初始值的动画。这意味着动画发生了两次。

Snip20161104_5.png

Autoreverses使得动画结束后又回到起始状态

5、repeatCount

和repeatCount比起来,repeatCount可以将动画重复两次(如下所示)或者任意次(你甚至可以使用像1.5这样的分数来完成一个半动画)。一旦动画达到它的最终值,他就会立马跳回到初始值并重新开始

Snip20161104_6.png

Repeat count可以让动画运行超过一次

6、repeat duration

和repeat count类似,但很少用到的就是repeat duration了。它将会根据给定的一个duration简单的重复动画(如下2秒所示)。经过一个repeat duration时,如果它小于动画的duration,那么动画就会提前结束(repeat duration之后结束)

Snip20161104_7.png

Repeat duration会让动画根据一个给定duration重复
这些都可以组合起来将一个反转动画重复多次或在一个给定的duration间重复。

Snip20161104_8.png

7、speed

一个跟时间相关有趣的属性是speed。通过设置duration为3秒,但是speed为2,动画快速的执行了1.5秒,因为它的速度是之前的两倍。

Snip20161104_9.png

速度为2时,动画执行速度是之前的两倍,所以3秒的动画只需要执行1.5秒
如果只是配置了一个简单的动画,那么你也可以分开使用beginTime和duration以达到相同的效果。但是使用speed属性的优点在于这两个事实:
1.动画的speed是分等级的。(hierarchical)

2.CAAnimation不是唯一一个实现CAMediaTiming的类。

Hierarchical speed
速度为2的动画组有一部分动画的速度为1.5,那么这个动画就是3倍于正常速度。

CAMediaTiming的其他实现
CAMediaTiming是CAAnimation实现的一个协议,但是CALayer(所有Core Animationlayers的基类)也实现了相同的协议,这就意味着你可以设置layer的speed为2.0,这样,所有加入到layer的动画运行都要快两倍。同样的,如果一个速度为3的动画加到一个速度为0.5的layer上,这个动画最终将会以1.5倍的常速运行。

为了控制动画或layer的速度,通常还可以设置speed为0,从而暂停动画。和timeOffset结合在一起时,可以通过像slider类似的控件控制动画,我们在下文中也会讲到。

刚开始timeOffset属性是非常奇怪的。正如名字所示那样,它对时间进行偏移(offset),从而计算出动画的状态。如下图所示。duration为3秒,offset为1秒的动画。

Snip20161104_10.png

你可以offset整个动画,但是动画所有部分任然会执行
动画开始运行时跳过第一秒进入从橙色到蓝色的过度,直接运行剩下的两秒,直到完全变蓝。然后动画直接跳回完全橙色的时候,并完成第一秒的颜色转换。这看起来有点像我们把动画的第一秒切下来,然后放到最后。

这个属性很少用,但是它可以和一个暂停的动画(speed=0)结合在一起控制’current time’。一个暂停的动画停留在第一帧。如果你观察offset动画每次的第一个颜色,你可以看到它的颜色值一秒就进入颜色转换。将time offset设置为其他值,你可以让那段时间进入转换。

控制动画时间
同时使用speed和timeOffset可以控制动画的当前时间。这几乎不会涉及到什么代码,但是概念却比较难以理解(我希望插图能有所帮助)。为了方便,我将动画的duration设为1.0。因为time offset是绝对值。这样做就意味着当time offset为0.0时,此时就是动画的0%处(动画开始),time offset为1.0时,就是动画的100%处(动画结束)。

8、代码展示遮罩效果动画

//  ViewController.m
#import "ViewController.h"
// 声明全局变量 设置图片的宽度和高度
static CGFloat const kImageViewWidth = 104;
static CGFloat const kImageViewHeight = 157;
@interface ViewController ()
{
    BOOL isAnimating;
    CAShapeLayer *maskLayer;;
}
@property (weak, nonatomic) IBOutlet UIImageView *topImageView;

@end

@implementation ViewController

- (void)viewDidLoad {
    [super viewDidLoad];

    CGRect rect = CGRectMake(0, 0, kImageViewWidth, kImageViewHeight);
    maskLayer = [CAShapeLayer layer];
    // 设置遮罩图层从贝塞尔曲线获取形状
    maskLayer.path = [UIBezierPath bezierPathWithRect:rect].CGPath;
    // 设置最上层图片的遮罩效果图层
    _topImageView.layer.mask = maskLayer;
}
- (IBAction)startAnimation:(id)sender {

    isAnimating = YES;
    CGRect rect = CGRectMake(0, 0, kImageViewWidth, kImageViewHeight);
    UIBezierPath *fromPath = [UIBezierPath bezierPathWithRect:rect];
    UIBezierPath *toPath = [UIBezierPath bezierPathWithRect:CGRectMake(0, 0, kImageViewWidth, 0)];
    // 遮罩图层从贝塞尔曲线获取形状
    maskLayer.path = fromPath.CGPath;
    // 遮罩覆盖的速度
    maskLayer.speed = 1;
    // 反复点击时产生动画效果
    [maskLayer removeAllAnimations];
    
    // 创建基础动画对象并设置动画属性
    CABasicAnimation *basicAnimation = [CABasicAnimation animationWithKeyPath:@"path"];
    basicAnimation.fromValue = (id)(fromPath.CGPath);
    basicAnimation.toValue = (id)(toPath.CGPath);
    basicAnimation.duration = 5;
    [maskLayer addAnimation:basicAnimation forKey:@"pathAnimation"];
    maskLayer.path = toPath.CGPath;
}


- (IBAction)resumeAnimation:(id)sender {
  
    if(isAnimating)
    {
        return;
    }
    isAnimating = YES;
    CFTimeInterval pausedTime = maskLayer.timeOffset;
    maskLayer.speed = 1.0;
    maskLayer.timeOffset = 0;
    maskLayer.beginTime = 0;
    CFTimeInterval timeSincePause = [maskLayer convertTime:CACurrentMediaTime() fromLayer:nil] - pausedTime;
    // 设置动画的开始时间为上一次的结束时间
    maskLayer.beginTime = timeSincePause;

}
- (IBAction)pauseAnimation:(id)sender {
    isAnimating = NO;
    CFTimeInterval pausedTime = [maskLayer convertTime:CACurrentMediaTime() fromLayer:nil];
    maskLayer.speed = 0;
    maskLayer.timeOffset = pausedTime;
}
@end

9、动画效果

遮罩动画.gif

本文文字部分来自http://www.cocoachina.com/programmer/20131218/7569.html

10、路径动画

//  ViewController.m
//  路径动画
//  Created by JackChen on 2016/11/4.
//  Copyright © 2016年 JackChen. All rights reserved.
#import "ViewController.h"
#import "CABasicAnimationDemo.h"
@interface ViewController ()
{
    UIBezierPath *smallOvalPath;
    UIBezierPath *ovalPath;
    
    CAShapeLayer *maskLayer;
    
    UIView  *recordButton;
    
    BOOL isReverse;
}
@end

@implementation ViewController

- (void)viewDidLoad {
    [super viewDidLoad];
   
    CGRect rect2 = CGRectMake(0, 0, 200, 200);
    smallOvalPath = [UIBezierPath bezierPathWithRoundedRect:rect2 cornerRadius:0];
    ovalPath = [UIBezierPath bezierPathWithRoundedRect:rect2 cornerRadius:100];
    
    maskLayer = [CAShapeLayer layer];
    maskLayer.path = smallOvalPath.CGPath;
    
    recordButton = [[UIView alloc]init];
    recordButton.backgroundColor = [UIColor redColor];
    recordButton.frame = CGRectMake(100, 100, 200, 200);
    recordButton.layer.mask = maskLayer;
    [self.view addSubview:recordButton];

}

-(void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event{
    
    [recordButton.layer removeAllAnimations];
    [maskLayer addAnimation:[CABasicAnimationDemo shapePathAnimationWithFromPath:smallOvalPath toPath:ovalPath reverse:!isReverse] forKey:@"pathAnimation"];
    isReverse = !isReverse;
}
@end

#import "CABasicAnimationDemo.h"
static CGFloat const kAnimationDuration = 1;
@implementation CABasicAnimationDemo
+ (CABasicAnimation *)shapePathAnimationWithFromPath:(UIBezierPath *)fromPath toPath:(UIBezierPath *)toPath reverse:(BOOL)isReverse
{
    if (isReverse) {
        return [CABasicAnimationDemo animationWithKeyPath:@"path" duration:kAnimationDuration fromValue:(id)fromPath.CGPath toValue:(id)toPath.CGPath];
    }else{
        return [CABasicAnimationDemo animationWithKeyPath:@"path" duration:kAnimationDuration fromValue:(id)toPath.CGPath toValue:(id)fromPath.CGPath];
    }
}
+ (CABasicAnimation *)animationWithKeyPath:(NSString *)keyPath
                                  duration:(CFTimeInterval)duration
                                 fromValue:(id)from
                                   toValue:(id)to{
    CABasicAnimation *animation = [CABasicAnimation animationWithKeyPath:keyPath];
    animation.duration = duration;
    animation.fromValue = from;
    animation.toValue = to;
    animation.removedOnCompletion = NO;
    animation.fillMode = kCAFillModeForwards;

    return animation;
}

@end

11、效果图

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

推荐阅读更多精彩内容

  • 在iOS中随处都可以看到绚丽的动画效果,实现这些动画的过程并不复杂,今天将带大家一窥ios动画全貌。在这里你可以看...
    每天刷两次牙阅读 8,461评论 6 30
  • 在iOS中随处都可以看到绚丽的动画效果,实现这些动画的过程并不复杂,今天将带大家一窥iOS动画全貌。在这里你可以看...
    F麦子阅读 5,092评论 5 13
  • 有一种通过CAAnimation实现的协议叫做CAMediaTiming,也就是CABasicAnimation和...
    YangPu阅读 1,990评论 1 5
  • 一、引言 在iOS开发中使用动画时,可以通过设置动画的duration、speed、begintime、offse...
    我是花老虎阅读 1,568评论 0 3
  • 这是我第一次翻译国外大神的文章。为了行文通顺,某些地方没有完全遵照原文。末尾附有自己的一些私货。原文链接如下:ht...
    我们是斗士阅读 1,458评论 0 0