首页 > 代码库 > 核心动画(CAKeyframeAnimation,CABasicAnimation)

核心动画(CAKeyframeAnimation,CABasicAnimation)

 

一,核心动画常用的三种例子

view的核心动画其体现就是把view按照指定好的路径进行运动,针对的是view的整体。 [view.layer addAnimation:动画路径 forKey:@“绑定动画路径的键值”];

A,view的整体按照指定好的路径进行运动,里面的子view固定在view不动情况:

1)创建需要显示的动画路径(动画路径可以是UIBezierPath,也可以是某个参数或点坐标等,如果是后者,则常常设置fromValue和toValue,通过它们来确定动画路径)

    CGPoint starPoint = CGPointMake(150, 200);

   CGPoint endPoint  = CGPointMake(400,550);

//    CGPoint ancholPoint = CGPointMake(endPoint.x + (starPoint.x - endPoint.x)/2.0, endPoint.y);

    UIBezierPath *animPath = [[UIBezierPath alloc] init];

    [animPath moveToPoint:starPoint];

    [animPath addLineToPoint:endPoint];

//    [animPath addQuadCurveToPoint:endPoint controlPoint:ancholPoint];

 

2)创建动画,把动画路径绑定到动画上去,并且配置一些其他动画属性;(路径时用到的是CAKeyframeAnimation)

    CAKeyframeAnimation *keyAnimation = [CAKeyframeAnimation animationWithKeyPath:@"position"];

    keyAnimation.path = animPath.CGPath;

    keyAnimation.removedOnCompletion = NO;

    keyAnimation.fillMode = kCAFillModeForwards;

    keyAnimation.duration = 5.0f;

    keyAnimation.repeatCount = MAXFLOAT;

    keyAnimation.autoreverses = YES;

    keyAnimation.timingFunction = [CAMediaTimingFunction functionWithName:kCAMediaTimingFunctionEaseIn];

3)设置动画代理并且绑定动画到遮盖上去

     keyAnimation.delegate = self;

      [view.layer addAnimation:keyAnimation forKey:@"keyAnim"];

或者(对于一些有规律简单的动画也可以使用CABasicAnimation)

    CABasicAnimation *keyAnimation = [CABasicAnimation animationWithKeyPath:@"position"];

    keyAnimation.fromValue = http://www.mamicode.com/[NSValue valueWithCGPoint:CGPointMake(150, 200)];

   keyAnimation.toValue = http://www.mamicode.com/[NSValue valueWithCGPoint:CGPointMake(400,550)];

   keyAnimation.removedOnCompletion = NO;

    keyAnimation.fillMode = kCAFillModeForwards;

    keyAnimation.duration = 5.0f;

    keyAnimation.repeatCount = MAXFLOAT;

    keyAnimation.autoreverses = YES;

    keyAnimation.timingFunction = [CAMediaTimingFunction functionWithName:kCAMediaTimingFunctionEaseIn];

    keyAnimation.delegate = self;

     [view.layer addAnimation:keyAnimation forKey:@"keyAnim"];

 

B,view的图层进行渲染绘画情况:

1)创建CAShapeLayer图层,配置图层属性及添加到view的图层上去

    CAShapeLayer *layer1 = [CAShapeLayer layer];

    layer1.fillColor = [UIColor clearColor].CGColor;

    layer1.strokeColor = [UIColor redColor].CGColor;

    layer1.lineWidth = 5;

[view.layer addSublayer:layer1];

2)创建需要显示的动画路径,及绑定到新创建的图层上去(动画路径可以是UIBezierPath,也可以是某个参数或点坐标等,如果是后者,则常常设置fromValue和toValue)

    UIBezierPath *path = [self bezierSinPathWithStartPoint:CGPointMake(0, 100) endPoint:CGPointMake(100, 100) topHeight:50];

    [path closePath];

    layer1.path = path.CGPath;

3)创建动画,把动画路径绑定到动画上去,并且配置一些其他动画属性;(路径时用到的是CAKeyframeAnimation)

   CABasicAnimation * anima = [CABasicAnimation animationWithKeyPath:@"strokeEnd"];

   anima.fromValue = http://www.mamicode.com/[NSNumber numberWithFloat:0.f];

   anima.toValue = http://www.mamicode.com/[NSNumber numberWithFloat:1.f];

    anima.duration = 4.0f;

    anima.repeatCount = MAXFLOAT;

   anima.timingFunction = UIViewAnimationOptionCurveEaseInOut;

4)设置动画代理并且绑定动画到新创建的图层上去

    anima.delegate = self;

    [layer1 addAnimation:anima forKey:@"strokeEndAniamtion2"];

 

 

C,view内部图层进行动画显示(即view内容本身固定的,只是慢慢显示或慢慢消失又或者按照指定轨迹显示内容),此时单纯的核心动画是无法做到的。解决方法如下:

1)创建CAShapeLayer作为view的遮盖:

CAShapeLayer *maskLayer = [CAShapeLayer layer];

view.layer.mask = maskLayer;

2)创建需要显示的动画路径(动画路径可以是UIBezierPath,也可以是某个参数或点坐标等,如果是后者,则常常设置fromValue和toValue,通过它们来确定动画路径)

  UIBezierPath *startPath =     ;

  UIBezierPath *endPath =     ;

3)遮盖的路径等于最后稳定显示时的路径,这个必须写,因为如果不写则会出现瞬间闪烁的BUG

maskLayer.path = endPath.CGPath;

4)创建动画,把动画路径绑定到动画上去,并且配置一些其他动画属性;(路径时用到的是CAKeyframeAnimation)

CABasicAnimation *animation = [CABasicAnimation animationWithKeyPath:@"path"];

animation.duration = 1.0;

animation.fromValue = http://www.mamicode.com/(__bridge id)starCycle.CGPath;//开始路径

animation.toValue   = (__bridge id)endCycle.CGPath;//结束路径

如果是单路径时,代码可以如下

// 关键路径动画

   CAKeyframeAnimation *animation = [CAKeyframeAnimation animationWithKeyPath:@"position"];

   animation.path = path.CGPath;

  [maskLayer addAnimation:animation forKey:@"position"];

 

5)设置动画代理并且绑定动画到遮盖上去

   animation.delegate = self;

   [maskLayer addAnimation:animation forKey:@"path"];

 

 

二,核心动画详述

ACABasicAnimation使用总结(CABasicAnimation是CAKeyframeAnimation里面的一种,CABasicAnimation用于只确定起点和终点或者是开始路径和结束路径的简单的放大缩小,平移,翻转,点与点,渲染等有规律的动画;CAKeyframeAnimation是用于设置一些比较复杂的动画,即可以随便自定义路径再绑定到path属性上去)

.fromValue = http://www.mamicode.com/起点值或者    .fromValue = http://www.mamicode.com/(__bridge id)starCycle.CGPath;//开始路径

.toValue      = 终点值            .toValue      =  (__bridge id)endCycle.CGPath;//结束路径

 

一,实例化:animationWithKeyPath,keypath如必须是下面几个

CABasicAnimation *transformAnima = [CABasicAnimation animationWithKeyPath:@"transform.rotation.y"];

 技术分享

position.x

position.y

position.z

position 位置(中心点的改变) [NSValue valueWithCGPoint:CGPointMake(300, 300)];或者keyAnimation.path = path.CGPath;

cornerRadius         圆角的设置 @(50)

backgroundColor 背景颜色的变化 (id)[UIColor purpleColor].CGColor

bounds 大小,中心不变 [NSValue valueWithCGRect:CGRectMake(0, 0, 200, 200)];

contents 内容,比如UIImageView的图片 imageAnima.toValue = http://www.mamicode.com/(id)[UIImage imageNamed:@"to"].CGImage;

opacity 透明度 @(0.7)

contentsRect.size.width 横向拉伸缩放 @(0.4)最好是0~1之间的

path 路径

locations

strokeEnd 从渲染开始到渲染结束

二,设定动画的属性和说明

duration             动画的时长

repeatCount     重复的次数。不停重复设置为 HUGE_VALF

repeatDuration    设置动画的时间。在该时间内动画一直执行,不计次数。

beginTime     指定动画开始的时间。从开始延迟几秒的话,设置为【CACurrentMediaTime() + 秒数】 的方式

timingFunction    设置动画的速度变化

autoreverses     动画结束时是否执行逆动画

fromValue     所改变属性的起始值

toValue         所改变属性的结束时的值

byValue             toValue = http://www.mamicode.com/fromValue+byValue

speed                  动画的速度

 

speed 属性注意点:

speed 改变动画的速度 可以直接设置动画上的speed属性,这样只有这个动画速度。或者在layer上设置speed属性,这样在该视图上的所有动画都提速,该视图上的所有子视图上的动画也会提速。

(1) 如果设置动画时间为4s,speed设置为2,则动画只需2s即可执行完。

(2)如果同时设置了动画的speed和layer 的speed,则实际的speed为两者相乘。

 

transformAnima.fromValue = http://www.mamicode.com/@(M_PI_2);或者 = [NSValue valueWithCATransform3D:CATransform3DMakeTranslation(0,10, 0)];

transformAnima.toValue = http://www.mamicode.com/@(M_PI);

transformAnima.timingFunction = [CAMediaTimingFunction functionWithName:kCAMediaTimingFunctionEaseInEaseOut];

transformAnima.autoreverses = YES;

transformAnima.repeatCount = HUGE_VALF;

transformAnima.beginTime = CACurrentMediaTime() + 2;延迟2秒

十分注意:

 1)//CATransform3DMakeTranslation(x,y,z)平移,那一个不为0则向那一个方向平移,X左Y下Z外为正方向;假如为(0,-10,0),则即是   将要动画的原点  在  原视图view原点   的上方10

2)如果animationWithKeyPath采用@"transform.rotation.y”样式,则fromValue,toValue,byValue采用@(数字或者角度);

      如果animationWithKeyPath采用@"transform”样式,则fromValue,toValue,byValue采用[NSValue valueWithCATransform3D:CATransform3DMakeTranslation(0, 10, 0)];

3)防止动画结束后回到初始状态,只需设置removedOnCompletion、fillMode两个属性就可以了。

transformAnima.removedOnCompletion = NO;

transformAnima.fillMode = kCAFillModeForwards;

 

解释:为什么动画结束后返回原状态?

首先我们需要搞明白一点的是,layer动画运行的过程是怎样的?其实在我们给一个视图添加layer动画时,真正移动并不是我们的视图本身,而是 presentation layer 的一个缓存。动画开始时 presentation layer开始移动,原始layer隐藏,动画结束时,presentation layer从屏幕上移除,原始layer显示。这就解释了为什么我们的视图在动画结束后又回到了原来的状态,因为它根本就没动过。

这个同样也可以解释为什么在动画移动过程中,我们为何不能对其进行任何操作。

所以在我们完成layer动画之后,最好将我们的layer属性设置为我们最终状态的属性,然后将presentation layer 移除掉。

 

fillMode属性的理解

该属性定义了你的动画在开始和结束时的动作。默认值是 kCAFillModeRemoved。

kCAFillModeRemoved 设置为该值,动画将在设置的 beginTime 开始执行(如没有设置beginTime属性,则动画立即执行),动画执行完成后将会layer的改变恢复原状。

kCAFillModeForwards 设置为该值,动画即使之后layer的状态将保持在动画的最后一帧,而removedOnCompletion的默认属性值是 YES,所以为了使动画结束之后layer保持结束状态,应将removedOnCompletion设置为NO。

kCAFillModeBackwards 设置为该值,将会立即执行动画的第一帧,不论是否设置了 beginTime属性。观察发现,设置该值,刚开始视图不见,还不知道应用在哪里。

kCAFillModeBoth 该值是 kCAFillModeForwards 和 kCAFillModeBackwards的组合状态

 

三,添加动画

[self.imageView.layer addAnimation:transformAnima forKey:@"A"];一个 CABasicAniamtion 的实例对象只是一个数据模型,需要绑定到layer上去

 组合动画的实现

CABasicAnimation *positionAnima = [CABasicAnimation animationWithKeyPath:@"position.y"];

positionAnima.fromValue = http://www.mamicode.com/@(self.imageView.center.y);

positionAnima.toValue = http://www.mamicode.com/@(self.imageView.center.y-30);

positionAnima.timingFunction = [CAMediaTimingFunction functionWithName:kCAMediaTimingFunctionEaseIn];

 CABasicAnimation *transformAnima = [CABasicAnimation animationWithKeyPath:@"transform.rotation.y"];

transformAnima.fromValue = http://www.mamicode.com/@(0);

transformAnima.toValue = http://www.mamicode.com/@(M_PI);

transformAnima.timingFunction = [CAMediaTimingFunction functionWithName:kCAMediaTimingFunctionEaseInEaseOut];

CAAnimationGroup *animaGroup = [CAAnimationGroup animation];

animaGroup.duration = 2.0f;

animaGroup.fillMode = kCAFillModeForwards;

animaGroup.removedOnCompletion = NO;

animaGroup.animations = @[positionAnima,transformAnima];

[self.imageView.layer addAnimation:animaGroup forKey:@"Animation"];

 

四,动画开始和结束时的事件

为了获取动画的开始和结束事件,需要实现协议 positionAnima.delegate = self;

代理方法实现

//动画开始时

- (void)animationDidStart:(CAAnimation *)anim

{

    NSLog(@"开始了");

}

 

//动画结束时

- (void)animationDidStop:(CAAnimation *)anim finished:(BOOL)flag

{

    //方法中的flag参数表明了动画是自然结束还是被打断,比如调用了removeAnimationForKey:方法或removeAnimationForKey方法,flag为NO,如果是正常结束,flag为YES。

    NSLog(@"结束了");

}

 其实比较重要的是有多个动画的时候如何在代理方法中区分不同的动画

两种方式

方式一:

如果我们添加动画的视图是全局变量,可使用该方法。

添加动画时,我们使用了

[self.imageView.layer addAnimation:animaGroup forKey:@"Animation"];

所以,可根据key来区分不同的动画

//动画开始时

- (void)animationDidStart:(CAAnimation *)anim

{

    if ([anim isEqual:[self.imageView.layer animationForKey:@"Animation"]]) {

        NSLog(@"动画组执行了");

    }

}

方式二

添加动画的视图是局部变量时,可使用该方法

添加动画给动画设置key-value

[positionAnima setValue:@"PositionAnima" forKey:@"AnimationKey"];

[transformAnima setValue:@"TransformAnima" forKey:@"AnimationKey"];

所以,可以根据key中不同的值来进行区分不同的动画

//动画结束时,没执行完一个动画,就会执行一次这个方法,因此可以通过判断valueForKey来判断是哪一个动画执行的

- (void)animationDidStop:(CAAnimation *)anim finished:(BOOL)flag

{

    if ([[anim valueForKey:@"AnimationKey"]isEqualToString:@"PositionAnima"]) {

        NSLog(@"位置移动动画执行结束");

    }

    else if ([[anim valueForKey:@"AnimationKey"]isEqualToString:@"TransformAnima"]){

        NSLog(@"旋转动画执行结束");

    }

}

 

解决循环引用问题

由于CAAnimation的delegate使用的strong类型,所以在全局变量如下设置时会产生循环引用的情况

解决方案

  • 声明一个单独的类实现delegate的回调

//.h

#import <UIKit/UIKit.h>

@interface AnimationDelegate : NSObject

@end

//.m

#import "AnimationDelegate.h"

@implementation AnimationDelegate

 - (void)animationDidStart:(CAAnimation *)anim

{

    NSLog(@"Animation Start");

}

//

- (void)animationDidStop:(CAAnimation *)anim finished:(BOOL)flag

{

    NSLog(@"Animation Stop");

}

 

- (void)dealloc

{

    NSLog(@"Delegate Dealloc");

}

@end

使用方式

self.animation.delegate = [[AnimationDelegate alloc]init];

  

ACAKeyFrameAnimation使用总结

CABasicAnimation算是CAKeyFrameAnimation的 特殊情况,即不考虑中间变换过程,只考虑起始点与目标点就可以了。而CAKeyFrameAnimation则更复杂一些,允许我们在起点与终点间自定义 更多内容来达到我们的实际应用需求!

 

一,设定动画的属性和说明

values           属性指明整个动画过程中的关键帧点。需要注意的是,起点必须作为values的第一个值。

path 用于指定整个动画所经过的路径的。需要注意的是,当values与path同时指定 时,path会覆盖values,即values属性将被忽略。

keyTimes 用以指定每个子路径(AB,BC,CD)的时间。如果你没有显式地对keyTimes进行设置,则系统会默认每条子路径的时间 为:ti=duration/n。其中首尾必须分别是0和1。

timeFunctions 用以指定时间函数,类似于运动的加速度

calculationMode 决定了物体在每个子路径下是跳着走还是匀速走,跟timeFunctions属性有点类似

1)timeFunctions属性

 kCAMediaTimingFunctionLinear//线性

 kCAMediaTimingFunctionEaseIn//淡入

 kCAMediaTimingFunctionEaseOut//淡出

 kCAMediaTimingFunctionEaseInEaseOut//淡入淡出

 kCAMediaTimingFunctionDefault//默认

2)calculationMode属性

 const kCAAnimationLinear//线性,默认

 const kCAAnimationDiscrete//离散,无中间过程,但keyTimes设置的时间依旧生效,物体跳跃地出现在各个关键帧上

 const kCAAnimationPaced//平均,keyTimes跟timeFunctions失效

 const kCAAnimationCubic//平均,同上

 const kCAAnimationCubicPaced//平均,同上

 

二,添加动画,(动画组执行是有顺序的)

CALayer  *rectLayer = [[CALayer alloc] init];

 CAKeyframeAnimation *positionAnima = [CAKeyframeAnimation animationWithKeyPath:@"position"];

//设定关键帧位置,必须含起始与终止位置

positionAnima.values = @[[NSValue valueWithCGPoint:CGPointMake(15,15)],

[NSValue valueWithCGPoint:CGPointMake(320 - 15,15)],

[NSValue valueWithCGPoint:CGPointMake(320 - 15, 100)],

[NSValue valueWithCGPoint:CGPointMake(15,  100)],

[NSValue valueWithCGPoint:CGPointMake(15,15)]];

//设定每个关键帧的时长,如果没有显式地设置,则默认每个帧的时间=总duration/(values.count - 1)

positionAnima.keyTimes =  @[[NSNumber numberWithFloat:0.0], 

[NSNumber numberWithFloat:0.6],

[NSNumber numberWithFloat:0.7],

[NSNumber numberWithFloat:0.8],

[NSNumber numberWithFloat:1]];

positionAnima.timingFunctions = @[[CAMediaTimingFunction functionWithName:kCAMediaTimingFunctionEaseInEaseOut],

[CAMediaTimingFunction functionWithName:kCAMediaTimingFunctionLinear],

  [CAMediaTimingFunction functionWithName:kCAMediaTimingFunctionLinear],

  [CAMediaTimingFunction functionWithName:kCAMediaTimingFunctionLinear]];

positionAnima.repeatCount = 1000;

positionAnima.autoreverses = NO;

positionAnima.calculationMode = kCAAnimationLinear;

positionAnima.duration = 4;

 [rectLayer addAnimation:rectRunAnimation forKey:@"QQQ"];

或者values 设置部分等效下面path设置

(这个是多条路径设置的)

CGMutablePathRef path = CGPathCreateMutable();

CGPathMoveToPoint(path, NULL, 15,  15);

CGPathAddLineToPoint(path, NULL, 320 - 15, 15);

CGPathAddLineToPoint(path, NULL, 320 - 15,100);

CGPathAddLineToPoint(path, NULL, 15,  100);

CGPathAddLineToPoint(path, NULL, 15, 15);

positionAnima.path = path;

CGPathRelease(path);

 (单路径设置采用如下)

 UIBezierPath *animPath = [[UIBezierPath alloc] init];

  [animPath moveToPoint:starPoint];起点

 [animPath addQuadCurveToPoint:endPoint controlPoint:ancholPoint];画线

暂停动画

-(void)pauseLayer:(CALayer*)layer

{

    CFTimeInterval pausedTime = [layer convertTime:CACurrentMediaTime() fromLayer:nil];

    layer.speed = 0.0;

    layer.timeOffset = pausedTime;

}

开始动画

-(void)resumeLayer:(CALayer*)layer

{

    CFTimeInterval pausedTime = [layer timeOffset];

    layer.speed = 1.0;

    layer.timeOffset = 0.0;

    layer.beginTime = 0.0;

    CFTimeInterval timeSincePause = [layer convertTime:CACurrentMediaTime() fromLayer:nil] - pausedTime;

    layer.beginTime = timeSincePause;

}

核心动画(CAKeyframeAnimation,CABasicAnimation)