iOS Animation - CABasicAnimation与CAKeyframeAnimation

来源:互联网 发布:厦门java培训多少钱 编辑:程序博客网 时间:2024/05/18 05:18

在 iOS 的 CAAnimation 中 CABasicAnimation、CAKeyframeAnimation的名字如此类似,方法调用也差不多,让较少使用到的开发者些许感到混淆,那我们就来讲讲这两者的区别和联系吧。

首先让我们看看他们的继承图:

CAAnimation继承图

很直观的,从上面可以看出CABasicAnimation与CAKeyframeAnimation都继承于CAPropertyAnimation。而CAPropertyAnimation提供了基本的动画属性设置keyPath、additive、cumulative。

  • iOS Animation-KeyPath值
keyPath值 说明 值类型 position 移动位置 CGPoint opacity 透明度 0-1 bounds 变大与位置 CGRect bounds.size 由小变大 CGSize backgroundColor 背景颜色 CGColor cornerRadius 渐变圆角 任意数值 borderWidth 改变边框border的粗细 任意数值 contents 改变layer内容(图片) CGImage transform.scale 缩放、放大 0.0-1.0 transform.rotation.x 旋转动画(翻转,沿着X轴) M_PI*n transform.rotation.y 旋转动画(翻转,沿着Y轴) M_PI*n transform.rotation.z 旋转动画(翻转,沿着Z轴) M_PI*n transform.translation.x 横向移动(沿着X轴) 任意数值 transform.translation.y 纵向移动(沿着Y轴) 任意数值

PS:后面的代码是用swift实现的,那些方法我们都可以用作公共类来调用。


单独解析

1)CABasicAnimation(基础动画)

CABasicAnimation提供了最基础的动画属性设置,是简单的keyframe动画性能。CABasicAnimation可以看做是一种CAKeyframeAnimation的简单动画,因为它只有头尾的关键帧(keyframe)。

我们可以创建一个CABasicAnimaiton的对象通过keyPath的方式。CABasicAnimation提供了fromValue、toValue、byValue的设置(插值)。它们三个属性定义了一个动画的轨迹,并且最少两个值不能为空。

当设置了CABasicAnimation的起点与终点值后,中间的值都是通过插值方式计算出来的,插值计算是通过timingFunction来指定,timingFunction默认为空,使用liner(匀速运动)。
例如,当我们设置了一个position的动画,设置了开始值PointA与结束值PointB,它们的运动先计算PointA与PointB的中间运动值PointCenter,而PointCenter是由timingFunction来指定值的,并且动画默认是直线匀速运动的。

我们来看看CABasicAnimation的一些简单实现的动画,例如移动、透明度、翻转等等。方法里面传入一个CALayer类或者子类就可以了。
PS:一般做简单动画,最快捷是用KVC给核心动画设置效果,图层里有哪些属性,我们就可以设置哪些属性,具体参考上面的KeyPath值。

//移动动画position    func addLayerAnimationPosition(layer: CALayer) {        let animation = CABasicAnimation(keyPath: "position")        //开始的位置        animation.fromValue = NSValue(CGPoint: layer.position)        //移动到的位置        animation.toValue = NSValue(CGPoint: CGPointMake(120, 200))        //持续时间        animation.duration = 3        //运动后的位置保持不变(layer的最后位置是toValue)        animation.removedOnCompletion = false        animation.fillMode = kCAFillModeForwards        //添加动画        layer.addAnimation(animation, forKey: "addLayerAnimationPosition")    }    //透明度 opacity    func addLayerAnimationOpacity(layer: CALayer) {        let animation = CABasicAnimation(keyPath: "opacity")        animation.fromValue = 1        animation.toValue = 0        animation.duration = 3        layer.addAnimation(animation, forKey: "addLayerAnimationOpacity")    }    //变大与位置 bounds    func addLayerAnimationBounds(layer:CALayer) {        let animation = CABasicAnimation(keyPath: "bounds")        animation.toValue = NSValue(CGRect:CGRectMake(130, 200, 140, 140))        animation.duration = 3        animation.repeatCount = 1        layer.addAnimation(animation, forKey: "addLayerAnimationBounds")    }    //由小变大 bounds.size    func addLayerAnimationBoundsSize(layer:CALayer) {        let animation = CABasicAnimation(keyPath: "bounds.size")        animation.fromValue = NSValue(CGSize: layer.bounds.size)        animation.toValue = NSValue(CGSize:CGSizeMake(layer.bounds.size.width+20, layer.bounds.size.height+20))        animation.duration = 3        animation.repeatCount = 1        layer.addAnimation(animation, forKey: "addLayerAnimationBoundsSize")    }    //改变颜色 backgroundColor    func addLayerAnimationBackgroundColor(layer:CALayer) {        let animation = CABasicAnimation(keyPath: "backgroundColor")        animation.toValue = UIColor.blueColor().CGColor        animation.duration = 3        animation.repeatCount = 1        layer.addAnimation(animation, forKey: "addLayerAnimationMargin")    }    //渐变圆角 cornerRadius    func addLayerAnimationCornerRadius(layer:CALayer) {        let animation = CABasicAnimation(keyPath: "cornerRadius")        animation.toValue = 30        animation.duration = 3        animation.repeatCount = 1        layer.addAnimation(animation, forKey: "addLayerAnimationCornerRadius")    }    //改变边框border的大小(图形周围边框,border默认为黑色), borderWidth    func addLayerAnimationBorderWidth(layer:CALayer) {        let animation = CABasicAnimation(keyPath: "borderWidth")        animation.toValue = 10        animation.duration = 3        animation.repeatCount = 1        layer.addAnimation(animation, forKey: "addLayerAnimationBorderWidth")    }    //改变layer内容(图片),注意如果想要达到改变内容的动画效果,首先在运行动画之前定义好layer的contents contents    func addLayerAnimationContents(layer:CALayer) {        let animation = CABasicAnimation(keyPath: "contents")        let toImage = UIImage.init(named: "通车辆设计矢量素材-06.png")?.CGImage        animation.toValue = toImage        animation.duration = 3        animation.repeatCount = 1        layer.addAnimation(animation, forKey: "addLayerAnimationBounds")    }    //缩放、放大 transform.scale    func addLayerAnimationTransformScale(layer:CALayer) {        let animation = CABasicAnimation(keyPath: "transform.scale")        //开始时的倍率        animation.fromValue = 1.0        //结束时的倍率        animation.toValue = 0.5        animation.duration = 3        animation.repeatCount = 1        animation.autoreverses = true        layer.addAnimation(animation, forKey: "addLayerAnimationScale")    }    //旋转动画(翻转,沿着X轴) transform.rotation.x    func addLayerAnimationTranformRotationX(layer: CALayer) {        let animation = CABasicAnimation(keyPath: "transform.rotation.x")        //旋转180度 = PI        animation.toValue = M_PI        animation.duration = 3        animation.repeatCount = 1        //这里我们可以添加可以不添加,添加一个缓慢进出的动画效果(int/out)。当不添加时,匀速运动,会使用kCAMediaTimingFunctionLinear;当添加时,layer会在开始和结束时比较缓慢        animation.timingFunction = CAMediaTimingFunction(name: kCAMediaTimingFunctionEaseInEaseOut)        layer.addAnimation(animation, forKey: "addLayerAnimationTranformRotationX")    }    //旋转动画(翻转,沿着Y轴) transform.rotation.y    func addLayerAnimationTranformRotationY(layer: CALayer) {        let animation = CABasicAnimation(keyPath: "transform.rotation.y")        //旋转180度 = PI        animation.toValue = M_PI        animation.duration = 3        animation.repeatCount = 1        //这里我们可以添加可以不添加,添加一个缓慢进出的动画效果(int/out)。当不添加时,匀速运动,会使用kCAMediaTimingFunctionLinear;当添加时,layer会在开始和结束时比较缓慢        animation.timingFunction = CAMediaTimingFunction(name: kCAMediaTimingFunctionEaseInEaseOut)        layer.addAnimation(animation, forKey: "addLayerAnimationTranformRotationY")    }    //旋转动画(沿着Z轴) transform.rotation.z    func addLayerAnimationTranformRotationZ(layer: CALayer) {        let animation = CABasicAnimation(keyPath: "transform.rotation.z")        //旋转360度 = PI*2        animation.toValue = M_PI*2        animation.duration = 3        animation.repeatCount = 1        //这里我们可以添加可以不添加,添加一个缓慢进出的动画效果(int/out)。当不添加时,匀速运动,会使用kCAMediaTimingFunctionLinear;当添加时,layer会在开始和结束时比较缓慢        animation.timingFunction = CAMediaTimingFunction(name: kCAMediaTimingFunctionEaseInEaseOut)        layer.addAnimation(animation, forKey: "addLayerAnimationTranformRotationZ")    }    //横向移动(沿着X轴) transform.translation.x    func addLayerAnimationTranformTranslationX(layer: CALayer) {        let animation = CABasicAnimation(keyPath: "transform.translation.x")        animation.toValue = 20        animation.duration = 3        animation.repeatCount = 1        //这里我们可以添加可以不添加,添加一个缓慢进出的动画效果(int/out)。当不添加时,匀速运动,会使用kCAMediaTimingFunctionLinear;当添加时,layer会在开始和结束时比较缓慢        animation.timingFunction = CAMediaTimingFunction(name: kCAMediaTimingFunctionEaseInEaseOut)        layer.addAnimation(animation, forKey: "addLayerAnimationTranformTranslationX")    }    //纵向移动(沿着Y轴) transform.translation.y    func addLayerAnimationTranformTranslationY(layer: CALayer) {        let animation = CABasicAnimation(keyPath: "transform.translation.y")        animation.toValue = 20        animation.duration = 3        animation.repeatCount = 1        //这里我们可以添加可以不添加,添加一个缓慢进出的动画效果(int/out)。当不添加时,匀速运动,会使用kCAMediaTimingFunctionLinear;当添加时,layer会在开始和结束时比较缓慢        animation.timingFunction = CAMediaTimingFunction(name: kCAMediaTimingFunctionEaseInEaseOut)        layer.addAnimation(animation, forKey: "addLayerAnimationTranformTranslationY")    }

2)CAKeyframeAnimation(帧动画)

CAKeyframeAnimation提供了通用的keyframe动画功能层的属性给我们使用,它允许我们设置一个特定的数组,这个数组是动画在运动时的值。也就是说,CAKeyframeAnimation支持动画的多个值设置。

CAKeyframeAnimation可以用两种方法定义:CGPath、数组values。

  • CGPath只对layer的anchorPoint或position属性起作用。
  • values比较灵活。

keyTimes是一个可选参数,它可以定义keyframe的每个部分,其值是0.0到1.0。keyTimes数组中的每个时间值都对应values中的每一帧,当keyTimes没有设置的时候,各个keyframe的时间是平分的。keyTimes数组中的每个元素定义了相应的keyframe的持续时间值作为动画的总持续时间的一小部分,每个元素的值必须大于、或等于前一个值。

keyframe动画有个重要属性:calculationMode。按照官方的说法,其值可以是’discrete’, ‘linear’, ‘paced’, ‘cubic’和’cubicPaced’。默认值是linear。
calculationMode是对anchorPoint和position进行的动画,当在平面坐标系中有多个离散点的时候,可以是离散,也可以是直线相连后进行插值计算,也可以使用圆滑的曲线将他们相连后进行插值计算。下面是对值的解释:

kCAAnimationLinear calculationMode的默认值,表示当关键帧为座标点的时候,关键帧之间直接直线相连进行插值计算;

kCAAnimationDiscrete 离散的,就是不进行插值计算,所有关键帧直接逐个进行显示;

kCAAnimationPaced 使得动画均匀进行,而不是按keyTimes设置的或者按关键帧平分时间,此时keyTimes和timingFunctions无效;

kCAAnimationCubic 对关键帧为座标点的关键帧进行圆滑曲线相连后插值计算,对于曲线的形状还可以通过tensionValues,continuityValues,biasValues来进行调整自定义,这里的数学原理是Kochanek–Bartels spline,这里的主要目的是使得运行的轨迹变得圆滑;

kCAAnimationCubicPaced 看这个名字就知道和kCAAnimationCubic有一定联系,其实就是在kCAAnimationCubic的基础上使得动画运行变得均匀,就是系统时间内运动的距离相同,此时keyTimes以及timingFunctions也是无效的.

下面我们来看看CAKeyframeAnimation的实例:

  1. 让一个layer左右晃动
    func addLayerKeyframeAnimationRock(layer:CALayer) {        let animation = CAKeyframeAnimation(keyPath: "position.x")        animation.values = [0, 10, -10, 0]        //additive设置为true是使Core Animation 在更新 presentation layer 之前将动画的值添加到 model layer 中去。可以看到上面的values是010,-100. 没有设置的话values=layer.position.x+0, layer.position.x+10, layer.position.x-10        animation.additive = true        animation.duration = 0.3        animation.repeatCount = 999999        layer.addAnimation(animation, forKey: "addLayerKeyframeAnimationRock")    }

2.让一个layer圆周(圆圈)运动

    func addLayerKeyframeAnimationOrbit(layer:CALayer) {        let animation = CAKeyframeAnimation(keyPath: "position")        let boundingRect = CGRectMake(layer.frame.origin.x, layer.frame.origin.y, 100, 100)        animation.path = CGPathCreateWithEllipseInRect(boundingRect,nil)        animation.duration = 3        animation.repeatCount = HUGE        //其值为kCAAnimationPaced,保证动画向被驱动的对象施加一个恒定速度,不管路径的各个线段有多长,并且无视我们已经设置的keyTimes        animation.calculationMode = kCAAnimationPaced        //kCAAnimationRotateAuto,确定其沿着路径旋转(具体要自己来体验,这里难解释)        animation.rotationMode = kCAAnimationRotateAuto        layer.addAnimation(animation, forKey: "addLayerKeyframeAnimationOrbit")    }

一句话总结

CABasicAnimation可看做是最多只有2个关键帧的CAKeyframeAnimation

0 0