iOS动画:CAKeyframeAnimation

网络中Core Animation类的继承关系图
 
 
 

属性简介

@interface CAKeyframeAnimation : CAPropertyAnimation
/* 提供关键帧数据的数组,数组中的每一个值都对应一个关键帧。根据动画类型(keyPath)的不同 ,
值的类型不同*/
@property(nullable, copy) NSArray *values;
/*基于点的属性的路径,即动画属性类型为CGPoint。如: position、anchorPoint、transform.translation等
如果为此属性指定非空值,则会忽略values属性*/
@property(nullable) CGPathRef path;
/* keyTimes的值与values中的值一一对应指定关键帧在动画中的时间点,取值范围为[0,1]。当keyTimes没有设置的时候,
各个关键帧的时间是平分的*/
@property(nullable, copy) NSArray*keyTimes;
/*指定每个关键帧之间的动画缓冲效果,timingFunctions.count = keyTimes.count-1*/
@property(nullable, copy) NSArray*timingFunctions;
/*关键帧间插值计算模式*/
@property(copy) NSString *calculationMode;
/*针对cubic 计算模式的动画,这些属性提供对插值方案的控制。每个*关键帧都可以具有与之相关的
张力、连续性和偏差值,这些值的范围在[-1,1]内(这定义了Kochanek-*Bartels样条,见http://en.wikipedia.org/wiki/Kochanek-Bartels_spline)。

*tensionValues控制曲线的“紧密度”(正值更紧,负值更圆)。
*continuityValues控制段的连接方式(正值表示锐角,负值表示倒角)。
*biasValues定义曲线发生的位置(正值在控制点之前移动曲线,负值在控制点之后移动它)。

*每个数组中的第一个值定义第一个控制点的切线的行为,第二个值控*制第二个点的切线,依此类推。任何未指定的值都默认为零
 *(如果未指定,则给出Catmull-Rom样条曲线)。 
*/
@property(nullable, copy) NSArray*tensionValues;
@property(nullable, copy) NSArray*continuityValues;
@property(nullable, copy) NSArray *biasValues;

/*定义沿路径动画的对象是否旋转以匹配路径切线*/
@property(nullable, copy) NSString *rotationMode;

@end

关键帧动画其实通过一组动画类型的值(或者一个指定的路径)和这些值对应的时间节点以及各时间节点的过渡方式来控制显示的动画。关键帧动画可以通过path属性和values属性来设置动画的关键帧。

通过path设置动画

1.1 path只能控制CGPoint类型的动画属性。如position、anchorPoint、transform.translation等
1.2 创建路径时所有的MoveTo、LineTo、CurveTo等方法是定的点都组成了动画的关键帧。可以通过keyTimes属性赋值来控制关键帧的时间点,通过timingFunctions属性控制关键帧间的动画
来控制动画的显示。
1.3 path属性的优先级高于values属性优先级。当path被赋非空值时,values属性的值将被忽略。
1.4 贝塞尔曲线可视工具:
http://yisibl.github.io/cubic-bezier/#.99,.01,1,.49

- (void)setPathAnimation
{
    CGMutablePathRef path = CGPathCreateMutable();
    //第一个关键帧  -100,-100
    CGPathMoveToPoint(path, NULL, -100, -100);
    //第二个关键帧  100,-100
    CGPathAddLineToPoint(path, NULL, 100, -100);
    //第三个关键帧  100,100
    CGPathAddLineToPoint(path, NULL, 100, 100);
    //第四个关键帧  -100,100
    CGPathAddLineToPoint(path, NULL, -100, 100);
    //第五个关键帧  -100,-100
    CGPathAddLineToPoint(path, NULL, -100, -100);
    
    CAKeyframeAnimation *animation = [CAKeyframeAnimation animation];
    animation.keyPath = @"transform.translation";
    animation.path = path;
    animation.duration = 4;
    animation.keyTimes = @[@(0),@(0.1),@(0.5),@(0.75),@(1)];
    animation.timingFunctions = @[[CAMediaTimingFunction functionWithControlPoints:1 :0.5 :0.5 :0.5],
                                  [CAMediaTimingFunction functionWithName:kCAMediaTimingFunctionEaseIn],
                                  [CAMediaTimingFunction functionWithName:kCAMediaTimingFunctionEaseOut],
                                  [CAMediaTimingFunction functionWithName:kCAMediaTimingFunctionDefault]];
    //动画结束后保持动画最后的状态,两个属性需配合使用
    animation.removedOnCompletion = NO;
    animation.fillMode = kCAFillModeForwards;
    
    CGPathRelease(path);
    [self.layer addAnimation:animation forKey:@""];
}

通过values设置动画

2.1 values指定了一组离散的关键帧,这些关键帧之间的需要通过插值来进行过渡。这些插值计算方式calculationMode设置。
2.2 网上看到有说calculationMode适用于anchorPoint 和 position等坐标点类型的属性,在查阅官方文档及代码测试,这种说法并不成立。calculationMode的插值计算方式同样适用于backgroundColor、opacity等非坐标点类型的动画属性

关键字属性
kCAAnimationLinear calculationMode的默认值,关键帧之间直接直线相连进行插值计算
kCAAnimationDiscrete 离散的,就是不进行插值计算,所有关键帧直接逐个进行显示。values数组长度比keyTimes数组长度小1,每一个keyTime对表示当前关键帧的起始时间和下一关键帧的起始时间,在keyTime对的时间内,物体停留在当前关键帧指定的位置
kCAAnimationPaced 插入线性关键帧,动画以恒定速度运行。同时忽略keyTimes和timingFunctions设置
kCAAnimationCubic 对关键帧为进行圆滑曲线相连后插值计算,对于曲线的形状还可以通过tensionValues, continuityValues, biasValues来进行自定义调整,这里的数学原理是Kochanek–Bartels spline,这里的主要目的是使得运行的轨迹变得圆滑;
kCAAnimationCubicPaced 看这个名字就知道和kCAAnimationCubic有一定联系,其实就是在kCAAnimationCubic的基础上使得动画运行变得均匀,就是系统时间内运动的距离相同,此时keyTimes以及timingFunctions也是无效的。对于曲线的形状也可以使用tensionValues,continuityValues,biasValues来进行调整
- (void)setValuesAnimation
{
    CGPoint center = self.view.center;
    
    CAKeyframeAnimation *animation = [CAKeyframeAnimation animation];
    animation.keyPath = @"position";
    animation.duration = 4;
    animation.repeatCount = CGFLOAT_MAX;
    animation.delegate = self;
    
    //    计算方式1: kCAAnimationLinear 直线相连进行插值计算
    animation.calculationMode = kCAAnimationLinear;
    animation.values = @[[NSValue valueWithCGPoint:CGPointMake(center.x-100, center.y-100)],
                         [NSValue valueWithCGPoint:CGPointMake(center.x+100, center.y-100)],
                         [NSValue valueWithCGPoint:CGPointMake(center.x+100, center.y+100)],
                         [NSValue valueWithCGPoint:CGPointMake(center.x-100, center.y+100)],
                         [NSValue valueWithCGPoint:CGPointMake(center.x-100, center.y-100)]];
    animation.keyTimes = @[@(0),@(0.25),@(0.5),@(0.75),@(1)];
    animation.timingFunctions = @[[CAMediaTimingFunction functionWithName:kCAMediaTimingFunctionLinear],
                                  [CAMediaTimingFunction functionWithName:kCAMediaTimingFunctionEaseIn],
                                  [CAMediaTimingFunction functionWithName:kCAMediaTimingFunctionEaseOut],
                                  [CAMediaTimingFunction functionWithName:kCAMediaTimingFunctionDefault]];
    
    //    计算方式2: kCAAnimationDiscrete。 无插值计算values.count = keyTimes.count-1;
    //    animation.calculationMode = kCAAnimationDiscrete;
    //    animation.values = @[[NSValue valueWithCGPoint:CGPointMake(center.x-100, center.y-100)],   //关键帧1开始时间0*duration=第0s
    //                          [NSValue valueWithCGPoint:CGPointMake(center.x+100, center.y-100)],   //关键帧2开始时间0.25*duration=第1s
    //                          [NSValue valueWithCGPoint:CGPointMake(center.x+100, center.y+100)],   //关键帧3开始时间0.5*duration=第2s
    //                          [NSValue valueWithCGPoint:CGPointMake(center.x-100, center.y+100)]];  //关键帧4开始时间0.75*duration=第3s 在第4秒动画结束
    //    animation.keyTimes = @[@(0),@(0.25),@(0.5),@(0.75),@(1)];
    
    //    计算方式3: kCAAnimationPaced。 关键帧间直线相连进行插值计算,动画以恒定速度运行,忽略keyTimes、timingFunctions;
    //    animation.calculationMode = kCAAnimationPaced;
    //    animation.values = @[[NSValue valueWithCGPoint:CGPointMake(center.x-100, center.y-100)],
    //                          [NSValue valueWithCGPoint:CGPointMake(center.x+100, center.y-100)],
    //                          [NSValue valueWithCGPoint:CGPointMake(center.x+100, center.y+100)],
    //                          [NSValue valueWithCGPoint:CGPointMake(center.x-100, center.y+100)],
    //                          [NSValue valueWithCGPoint:CGPointMake(center.x-100, center.y-100)]];
    
    //    计算方式4: kCAAnimationCubic。 关键帧间曲线相连进行插值计算
    //    animation.calculationMode = kCAAnimationCubic;
    //    animation.values = @[[NSValue valueWithCGPoint:CGPointMake(center.x-100, center.y-100)],
    //                          [NSValue valueWithCGPoint:CGPointMake(center.x+100, center.y-100)],
    //                          [NSValue valueWithCGPoint:CGPointMake(center.x+100, center.y+100)],
    //                          [NSValue valueWithCGPoint:CGPointMake(center.x-100, center.y+100)],
    //                          [NSValue valueWithCGPoint:CGPointMake(center.x-100, center.y-100)]];
    //    //然而知道该如何精确操作
    //    animation.tensionValues = @[@(1),@(0),@(0),@(0),@(0)];
    //    animation.continuityValues = @[@(-1),@(1),@(-1),@(1),@(-1)];
    //    animation.biasValues= @[@(1),@(1),@(1),@(1),@(1)];
    
    //    计算方式5: kCAAnimationCubic。关键帧间曲线相连进行插值计算,动画以恒定速度运行,忽略keyTimes、timingFunctions;
    //   animation.calculationMode = kCAAnimationCubicPaced;
    //    animation.values = @[[NSValue valueWithCGPoint:CGPointMake(center.x-100, center.y-100)],
    //                          [NSValue valueWithCGPoint:CGPointMake(center.x+100, center.y-100)],
    //                          [NSValue valueWithCGPoint:CGPointMake(center.x+100, center.y+100)],
    //                          [NSValue valueWithCGPoint:CGPointMake(center.x-100, center.y+100)],
    //                          [NSValue valueWithCGPoint:CGPointMake(center.x-100, center.y-100)]];
    //    animation.keyTimes = @[@(0),@(0.25),@(0.5),@(0.75),@(1)];
    //    animation.timingFunctions = @[[CAMediaTimingFunction functionWithName:kCAMediaTimingFunctionLinear],
    //                                   [CAMediaTimingFunction functionWithName:kCAMediaTimingFunctionEaseIn],
    //                                   [CAMediaTimingFunction functionWithName:kCAMediaTimingFunctionEaseOut],
    //                                   [CAMediaTimingFunction functionWithName:kCAMediaTimingFunctionDefault]];
    //    //然而知道该如何精确操作
    //    animation.tensionValues = @[@(1),@(0),@(0),@(0),@(0)];
    //    animation.continuityValues = @[@(-1),@(1),@(-1),@(1),@(-1)];
    //    animation.biasValues= @[@(1),@(1),@(1),@(1),@(1)];
    
    [self.layer addAnimation:animation forKey:@""];
    
    if (!_displayLink) {
        [self.displayLink addToRunLoop:[NSRunLoop currentRunLoop] forMode:NSDefaultRunLoopMode];
    }
}

如有大神碰巧路过,发现问题请进行指正

demo地址:
https://gitee.com/dbmxl/KeyframeAnimation

参考文章:
http://www.iosxxx.com/blog/2015-11-01-coreanimationdong-hua-ru-men.html
https://www.jianshu.com/p/22333040616e

原文地址:https://www.cnblogs.com/Free-Thinker/p/11262722.html