iOS学习开发

CoreAnimation之动画实现

2017-11-24  本文已影响0人  LotusLee

文章结构

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

动画作用域

所有的动画都是作用于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

- (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

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

示例代码:斜射火箭

- (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添加的动画。应该都学过胡克定律,不记得的可以百度下。为了更好的理解,最好先去了解下。

- (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

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

    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

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

- (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

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

- (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哦😄

上一篇 下一篇

猜你喜欢

热点阅读