CoreAnimation之动画实现

文章结构

  • 动画作用域
  • 常用动画属性
  • 动画类结构
  • 动画类的讲解

本文主要讲解动画基本知识,只要搞懂了基础,看似炫酷的复杂动画其实就不难,复杂的动画都是由一个个简单的动画拼接而成的,其实任何事物也是如此。

动画作用域

所有的动画都是作用于CALayer这个类或者子类,视图都自带一个默认的CALayer类型的图层,我们要做的动画就是改变这个图层上的一些属性来达到目的,哪些属性可以动画,开发文档里都有说明,而且属性值的类型需要转换成特定的数值类型才可以,我做了一个表格汇总如下:


比如我们要在x轴上平移,则需要修改position属性的x坐标,坐标是CGFloat类型的数据,我们需要把它转成NSNumber类型,然后赋值给代表属性变化的变量,如CABasicAnimation和fromeValue、toValue、byValue等。

常用动画属性

先来看一个代码
CABasicAnimation *animation_1 = [CABasicAnimation animationWithKeyPath: @"position.y"];
这是一个设置在y轴上平移的代码,最后一个参数是不是感觉好无语,我要平移或者旋转该如何设置呢?其实用得最多的莫过于平移和旋转,可以在x轴、y轴或者同时在两个轴上移动,都是通过设置position这个属性来设置的。旋转最常用的就是transform.rotation.z沿z轴旋转,transform的其他设置还有平移transform.translation、缩放transform.scale,分别也有相对应的x、y、z方向,可以自己玩玩查看相对应效果。
其实,还有好多动画的属性比如颜色,透明度等,多得跟米一样。一般情况我们都是通过点击某个控件后,让其实现一些炫酷的效果,这些效果用得最多的就是平移和缩放、旋转
等的结合。

动画类结构

先来看一张图,如下:


动画类结构

所有的动画都是继承CAAnimation,此为所有动画的父类,而且遵循CAMediaTiming协议,箭头代表继承关系。其中,CAPropertyAnimation是一个抽象类,不能直接使用,一般都是使用它的子类应用到layer中。

动画类的讲解

主要讲解常用的一些属性

CABasicAnimation

  • fromValue 属性的开始值
  • toValue 属性的结束值
  • byValue 属性变化的相对值,可以理解为toValue减去fromValue,当不需要明确开始和结束的具体值时,可以只设置该属性
    该类是最基础的动画类,只包含两点间的动画,使用方法一:设置layer某个属性的开始值fromValue,结束值toValue;方法二:只设置byValue;方法三:设置fromValue和byValue,相当于toValue等于fromValue加上byValue;方法四:设置toValue和byValue,相当于fromValue等于toValue减去byValue。常用的搭配使用就这四种了,也是比较容易理解的。
    使用示例:实现在y轴上的动画
- (CABasicAnimation *)configureBasicTranslationAnimationFromValue:(id)fromeValue toValue:(id)toValue byValue:(id)byValue{
    //平移动画
    CABasicAnimation *basicAnimation = [CABasicAnimation animationWithKeyPath: @"position.y"];
    
    //这个if clause只是为了演示方便,项目在中请根据实际情况处理相关逻辑
    if (byValue) {
        //该属性表示相对值,如果不需要明确开始和结束的值,可以使用该属性代替fromValue和toValue
        basicAnimation.byValue = byValue;
    }else{
        basicAnimation.fromValue = fromeValue;
        basicAnimation.toValue = toValue;
    }
    
    //fillMode和removedOnCompletion搭配,最常用的就是保持动画结束时候的状态,除非有必要,否则不要这样设置,会有离屏渲染,增加性能损耗
//    basicAnimation.fillMode = kCAFillModeForwards;
//    basicAnimation.removedOnCompletion = NO;
    
    
    
    if (_animationType != AnimationTypeGroup) {
        basicAnimation.duration = 1.0;//动画时间
        basicAnimation.repeatCount = 1;
        [_animationView.layer addAnimation:basicAnimation forKey:nil];
    }
    
    return basicAnimation;
}

示例代码:自转

- (CABasicAnimation *)configureCenterRotationAnimation{
    //旋转动画:最常用的就是围绕z轴旋转transform.rotation.z,transform的其他设置还有平移transform.translation、缩放transform.rotation,分别也有相对应的x、y、z方向,可以自己玩玩查看相对应效果
    CABasicAnimation *rotateAnimation = [CABasicAnimation animationWithKeyPath:@"transform.rotation.z"];
    rotateAnimation.fromValue = @(0);
    rotateAnimation.toValue = @(M_PI * 2);
    
    
    if (_animationType != AnimationTypeGroup) {
        rotateAnimation.duration = 1.0;
        rotateAnimation.repeatCount = MAXFLOAT;//MAXFLOAT无限循环
        [_animationView.layer addAnimation:rotateAnimation forKey:nil];
    }
    
    return rotateAnimation;
}

CAKeyframeAnimation

帧动画,可以包含多个点,或者指定动画的路径。
注意:如果你的试图动画是由多个点组成的建议使用路径来代替,方便简单而且不容易出错。

  • values 是一个数组包含动画轨迹的各个值。
  • path 动画的路径,一般设置了values就不要设置path。
  • keyTimes 一个数组,代表各个动画节点的时间点,而且是一个递增的NSNumber类型数据,而且元素个数必须和动画节点数一致,否则可能达不到你要的动画效果。如果calculationMode = kCAAnimationLinear or kCAAnimationCubic,keytimes属性中的第一个数必须是0,最后一个必须是1;如果是kCAAnimationDiscrete,keytimes属性中的第一个数必须是0,最后一个数必须是1,而且keytimes属性的数组元素个数比values中的多一个;如果是kCAAnimationPaced or kCAAnimationCubicPaced,则该属性不起作用。
  • timingFunctions 这个应该不陌生了,比如你的动画由A->B->C->D,则可以定义A->B,B->C,C->D之间的动画效果,先快后慢还是先慢后快等效果。如果你的动画节点即values属性或者path路径的节点数为n,则该属性个数应该为n-1个。
  • calculationMode 据我的理解,该属性表示帧与帧之间的转换模式,比如设置成kCAAnimationCubic表示从AB段转换到BC段时,有一个弧度,而不是一个直角似的生硬,前提是这连个线程不在同一条直线上,要不然看不出效果。
  • rotationMode 旋转模式,是否打开自旋转,可以这样比喻地球围绕太阳公转,地球也有自转。若不需要自转则不用设置该属性

示例代码:斜射火箭

- (void)configureKeyframesAnimation{
    _animationView.transform =CGAffineTransformMakeRotation(M_PI_4);
    CAKeyframeAnimation *animation = [CAKeyframeAnimation animationWithKeyPath:@"position"];
    
    CGPoint point_1 = CGPointMake(_animationView.center.x, _animationView.center.y);
    CGPoint point_2 = CGPointMake(point_1.x + 20, point_1.y - 20);
    CGPoint point_3 = CGPointMake(point_2.x + 130, point_2.y - 130);
    CGPoint point_4 = CGPointMake(point_3.x, point_1.y);
    
    NSValue *value_1 = [NSValue valueWithCGPoint:point_1];
    NSValue *value_2 = [NSValue valueWithCGPoint:point_2];
    NSValue *value_3 = [NSValue valueWithCGPoint:point_3];
    NSValue *value_4 = [NSValue valueWithCGPoint:point_4];
    animation.values = @[value_1, value_2,value_3,value_4];
    
    animation.repeatCount = 1;
    animation.duration = 3.0f;
    
    //一般来讲该属性的count必须和values属性的count一致,且后一个数必须大于或者等于后一个数
    //如果calculationMode =  kCAAnimationLinear or kCAAnimationCubic,keytimes属性中的第一个数必须是0,最后一个必须是1;如果是kCAAnimationDiscrete,keytimes属性中的第一个数必须是0,最后一个数必须是1,而且keytimes属性的数组元素个数比values中的多一个;如果是kCAAnimationPaced or kCAAnimationCubicPaced,则该属性不起作用。
    animation.keyTimes = @[@(0.0), @(0.2), @(0.6), @(1.0)];
    animation.calculationMode = kCAAnimationCubic;
    
    animation.delegate = self;
    [_animationView.layer addAnimation:animation forKey:nil];
    
}

示例代码:路径动画

- (CAKeyframeAnimation *)configurePathRotationAnimation{
    //帧动画
    //按照路径进行动画,该例子中是围绕一个圆进行动画
    CGMutablePathRef path = CGPathCreateMutable();
    CGPathAddArc(path, NULL, _animationView.center.x, _animationView.center.y, 80, M_PI * 2, 0, true);
    CAKeyframeAnimation *keyAnimation = [CAKeyframeAnimation animationWithKeyPath:@"position"];
    keyAnimation.path = path;
    CGPathRelease(path);
    
    //该属性表示动画结束后会逆向原路返回远点
    //    keyAnimation.autoreverses = YES;
    
    //旋转模式,是否打开自旋转,可以这样比喻地球围绕太阳公转,地球也有自转。若不需要自转则不用设置该属性
    keyAnimation.rotationMode = kCAAnimationRotateAuto;
    
    //匀速,其他效果自己玩
    keyAnimation.calculationMode = kCAAnimationPaced;
    
    if (_animationType != AnimationTypeGroup) {
        keyAnimation.duration = 5.0;
        keyAnimation.repeatCount = 1;
        [_animationView.layer addAnimation:keyAnimation forKey:nil];
    }
    
    return keyAnimation;
}

CASpringAnimation

弹簧动画,这是ios9添加的动画。应该都学过胡克定律,不记得的可以百度下。为了更好的理解,最好先去了解下。

  • mass 质量:值越大,弹性效果越强,振动次数越多,settlingDuration越大。
  • stiffness 弹簧的劲度(刚度):值越大,振动次数越少,settlingDuration越小。
  • damping 阻尼:值越大,settlingDuration 越小。
  • initialVelocity 初速度:有初速度的话,settling time肯定越大, 正负值只是代表方向。
  • settlingDuration 据我的理解,这是根据你设定的以上四个参数,计算出来的弹簧从开始运动到静止所需要的时间,该值只读的,这个不一定和你设定的动画时间一样,比如需要10秒弹簧才停止振动,但是你给的动画时间就3秒,之后就停止,这也是可以的。
    mass、stiffness、damping、initialVelocity这几个参数都会影响到settlingDuration的值。
    示例代码:
- (void)configureSpringAnimation{
    //弹簧动画
    CASpringAnimation *springAnimation = [CASpringAnimation animationWithKeyPath:@"position.x"];
    springAnimation.duration = 3.0;
    springAnimation.repeatCount = 1.0;
    springAnimation.fromValue = @(50);
    springAnimation.toValue = @([UIScreen mainScreen].bounds.size.width - 50);
    springAnimation.delegate = self;
    
    //阻尼:值越大 settling time 越小
    springAnimation.damping = 1;
    
    //质量:值越大 弹性效果越强 振动次数越多 settling time越大
//    springAnimation.mass = 0.5;
    
    //弹簧的劲度(刚度):值越大 振动次数越少 settling time越小
//    springAnimation.stiffness = 10;
    
    //初速度 有初速度的话 settling time肯定越大, 正负值只是代表方向
    springAnimation.initialVelocity = -5;
    
    [_animationView.layer addAnimation:springAnimation forKey:nil];
    
    NSLog(@"settling time is %f", springAnimation.settlingDuration);
}

CATransition

转场动画,可以应用在两个控制器之间的转换,或者两个试图之间的转换。

  • type 转场的类型,具体效果可以运行代码试试,最直观
  • subtype 定义转场如何地进行
    示例代码:两个控制器之间的转换,我在Demo里已经注释掉了,如果想看效果,首先要把storybaord上的从tableviewcell到AnimationViewController 的seague去掉。
    AnimationViewController *vc = [self.storyboard instantiateViewControllerWithIdentifier:@"AnimationViewController"];
    
    //转场动画
    CATransition *transition = [CATransition animation];
    
    //切换其他值试下效果
    transition.type = kCATransitionReveal;
    
    //打开这个玩玩
//    transition.subtype = kCATransitionFromTop;
    transition.duration = 0.25;
    [self.navigationController.view.layer addAnimation:transition forKey:nil];
    [self.navigationController pushViewController:vc animated:NO];

CAAnimationGroup

组动画,就是一些动画的集合,那些复杂的动画就是通过组动画来实现的。

  • animations 数组:各个独立的动画。注意各个独立的动画之间不要有冲突。
    示例代码:
- (void)configureGroupAnimation{
    
    //组合动画:动画间不能有冲突,又要按圆路径动画,又要沿y轴移动,这很明显不行的,可以打开数组的那个注释试下效果.
    //如果还 需要其他 动画,可以继续添加.
    CABasicAnimation *basicAnimation = [self configureBasicTranslationAnimationFromValue:nil toValue:nil byValue:@(-200)];
    CABasicAnimation *centerRotateAnimation = [self configureCenterRotationAnimation];
    //CAKeyframeAnimation *pathAnimation = [self configurePathRotationAnimation];
    
    CAAnimationGroup *groupAnimations = [CAAnimationGroup animation];
    groupAnimations.animations = @[basicAnimation, centerRotateAnimation/*, pathAnimation*/];
    groupAnimations.duration = 2.0;
    groupAnimations.repeatCount = 1;
    
    [_animationView.layer addAnimation:groupAnimations forKey:nil];
}

CAAnimationDelegate

动画代理,如果你想监测动画什么时候开始和结束可以设置代理。不要说不会设置代理哦-_-

CAMediaTiming

所有动画类都遵从的协议,通过设置这里的属性你可以开始或者暂停动画、设置动画时间、设置动画的开始时间、动画的重复次数等,下面一一讲解下常用属性怎么用。

  • beginTime 动画开始时间。如果你的视图有多个动画,则可以通过设置该属性让一些动画先开始,再继续其他动画。
  • duration 整个动画的时长
  • speed 动画的速度,一般该属性和timeOffset、beginTime这两个属性来实现继续或者暂停动画。很少有暂停动画这种实现的,项目里没用过。
  • repeatCount 动画重复次数
  • autoreverses 动画结束后是否原路返回
  • fillMode 该属性最常用的就是保持动画结束时的状态,需要和CANimaiton类的removedOnCompletion属性配合使用。除非有必要,否则最好不要这么做,会导致离屏渲染,性能损耗。
    示例代码:暂停动画
- (void)stopAnimation:(CALayer *)layer{
    
    CFTimeInterval pauseTime = [layer convertTime:CACurrentMediaTime() toLayer:nil];
    layer.speed = 0.0;
    layer.timeOffset = pauseTime;
    
    NSLog(@"begin time: %f, pause time: %f", layer.beginTime, pauseTime);
    
}

示例代码:继续动画

- (void)resumeAnimation:(CALayer *)layer{
    CFTimeInterval pauseTime = layer.timeOffset;
    layer.speed = 1.0;
    layer.timeOffset = 0.0;
    layer.beginTime = 0.0;
    
    CFTimeInterval currentTime = [layer convertTime:CACurrentMediaTime() toLayer:nil];
    CFTimeInterval timeSincePause = currentTime  - pauseTime;
    
    NSLog(@"pause time: %f, current time: %f, timeoffset : %f", pauseTime, currentTime, timeSincePause);
    layer.beginTime = timeSincePause;
}

示例代码:beginTime使用,一个动画结束再开始另外一个动画

- (void)configureOnebyoneAnimation{
    
    //上升
    CABasicAnimation *animation_1 = [CABasicAnimation animationWithKeyPath: @"position.y"];
    animation_1.byValue = @(-200);
    animation_1.duration = 1.0;//动画时间

    //下降
    CABasicAnimation *animation_2 = [CABasicAnimation animationWithKeyPath:@"position.y"];
    animation_2.fromValue = @(_animationView.frame.origin.y - 200);
    animation_2.toValue = @(_animationView.frame.origin.y);
    animation_2.duration = 1.0;
    animation_2.beginTime = CACurrentMediaTime() + 1;

    //无限自转
    CABasicAnimation *animation_3 = [CABasicAnimation animationWithKeyPath:@"transform.rotation.z"];
    animation_3.fromValue = @(0);
    animation_3.toValue = @(M_PI * 2);
    animation_3.duration = 0.2;
    animation_3.repeatCount = MAXFLOAT;
    animation_3.beginTime = CACurrentMediaTime() + 1;
    
    //回到原点去除动画
    [NSTimer scheduledTimerWithTimeInterval:2 repeats:NO block:^(NSTimer * _Nonnull timer) {
        [_animationView.layer removeAllAnimations];
    }];
    
    [_animationView.layer addAnimation:animation_1 forKey:nil];
    [_animationView.layer addAnimation:animation_2 forKey:nil];
    [_animationView.layer addAnimation:animation_3 forKey:nil];
}

最后Demo在此,欢迎下载交流,觉得有用的话,别忘记start哦😄

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

推荐阅读更多精彩内容

  • Android 自定义View的各种姿势1 Activity的显示之ViewRootImpl详解 Activity...
    passiontim阅读 171,431评论 25 707
  • iOS动画篇之CoreAnimation动画 9月 22, 2016发布在Objective-C App如果想被大...
    白水灬煮一切阅读 2,002评论 0 0
  • 1、属性选择器:id选择器 # 通过id 来选择类名选择器 . 通过类名来选择属性选择器 ...
    Yuann阅读 1,617评论 0 7
  • 都是老多人推的大红文了,可CP的相处模式,我特么最最不喜欢了,完全踩中雷点!!一见为欲,再见为爱,先来鞭子后给糖,...
    道伊裘阅读 1,172评论 0 1
  • 今天犯了一个错误,叫做不分主次,错了,是26年都在犯这个错误,今天才被点醒了! 对待人生,从未分过主次;面临选择,...
    海豚的世界阅读 413评论 0 0