引言
上一篇博客我们已经讨论了Core Animation中时间的处理,以及CAMediaTiming协议。本篇博客我们就来看一下另外一个和时间有关的机制--缓冲。
缓冲会让动画看起来更平滑更自然,我们在本篇博客将会体验一下CAAnimation提供的缓存函数,UIView提供的缓存函数,以及关键帧动画如何设置缓冲函数。
动画缓冲
动画速度
说到缓冲我们不得不提到速度,对于物体来讲我们都知道速度=距离/时间,对于动画也是一样的。
velocity = change / time
只不过移动距离替换为了外观的变化,时长就还是值动画一次迭代的时长。
例如说我们给position做动画,这个例子很形象,和物体移动几乎一样,但是实际上它可以是任何东湖,比如颜色,比如透明度,比如旋转角度。
但是上面的等式其实及基于一个假设,假设动画在执行的过程中速度都是恒定不变的,这种恒定速度的动画我们称之为“线性动画”,这是动画缓冲里面最简单的一种,但是呢也最不真实。
无论是行驶的小汽车还是掉落的小球事实上它都不会从一开始到结束都以相同的速度前进,那小汽车来说,通常来讲它的速度应该是从0逐渐增加然后再保持匀速,快要到达终点时再逐渐减小到0。
那么在动画中我们该如下实现这种效果呢,看起来需要通过物理引擎来模拟这些东西,听来有点复杂,不过事实上Core Animation内嵌了一些列的标准缓冲函数,我们可以直接使用它们来达到想要的效果。
CAMediaTimingFunction
为了使用 Core Animation 提供的缓动函数,我们需要设置 CAAnimation 的 timingFunction 属性。timingFunction 是 CAMediaTimingFunction 类型,并且提供了一个非常简便的初始化方法。
public convenience init(name: CAMediaTimingFunctionName)
CAMediaTimingFunctionName有以下五个类型:
- .linear:创建一个线性的计时函数,默认类型,适用那些保持匀速运动的物体。
- .easeIn:创建一个慢慢加速,然后突然停止的缓冲函数,它对于一个自由落体或者是发射子弹打击目标来说很适用。
- .easeOut:和.easeIn相反,一开始全速然后慢慢减速到停止,有一个削弱的过程,适合关门这样的场景。
- .easeInEaseOut:创建一个慢慢加速然后再慢慢减速的缓存函数,通常的动画我们都会采用这个函数。
- .default:其实它和.easeInEaseOut非常类似,只不过它的加速和减速的过程都稍微慢一些。
这里面为什么叫做default但它又不是默认值呢,其实如果我们显式的创建CAAnimation时,.linear为默认选项,但是当我们不创建CAAnimation,图层的默认动画缓冲则是.default类型。
下面我们来写一个简单的案例,体会一下缓冲函数的作用,代码如下:
override func viewDidLoad() {super.viewDidLoad()// Do any additional setup after loading the view.timeFunction()}let colorLayer = CALayer()func timeFunction() {self.view.layer.addSublayer(colorLayer)colorLayer.frame = CGRect(x: 50, y: 50, width: 100, height: 100)colorLayer.backgroundColor = UIColor.red.cgColor}override func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent?) {CATransaction.begin()CATransaction.setAnimationDuration(1.0)CATransaction.setAnimationTimingFunction(CAMediaTimingFunction(name: CAMediaTimingFunctionName.easeInEaseOut))colorLayer.position = touches.first!.location(in: self.view)CATransaction.commit()}
效果如下:
案例中我们采用了隐式动画设置缓冲函数,显式动画也是一样的代码如下:
let animation = CABasicAnimation()animation.keyPath = "transform.position"animation.toValue = NSValue(cgPoint: touches.first!.location(in: self.view))animation.duration = 2.0animation.timingFunction = CAMediaTimingFunction(name: CAMediaTimingFunctionName.easeInEaseOut)colorLayer.add(animation, forKey: nil)
UIKit 中的动画缓冲
我们在使用UIView的类方法给视图添加动画时,其中有一个options可选参数,不过之前我们并没有注意到它。事实上它就是UIKit为我们提供的动画缓存函数,有以下几个可以选择的类型:
- .curveEaseInOut:和图层的.easeInEaseOut对应,慢进慢出。
- .curveEaseIn:和图层的.easeIn对应,慢进。
- .curveEaseOut:和图层的.easeOut对应,慢出。
- .curveLinear:和图层的.linear对应,线性匀速做动画。
注意这里面只有这四个选项,没有与图层.default对应的选项,我们在具体的代码中来体验一下它的效果,代码如下:
func kitTimeFunction() {self.view.addSubview(colorView)colorView.frame = CGRect(x: 50, y: 50, width: 100, height: 100)colorView.backgroundColor = UIColor.yellow}override func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent?) {UIView.animate(withDuration: 1.0, delay: 0.0,options: .curveEaseInOut) {self.colorView.center = touches.first!.location(in: self.view)}}
效果如下:
可以看到它的效果和上面我们对图层设置慢进慢出效果是一样的。
关键帧动画和缓冲
关键帧动画的缓存相对于其它动画有一些特别,所以我们拎出来单独说它。就之前博客提到的颜色变化的动画来说,它有5个关键帧,我们在应用缓冲时,则需要设置4个缓冲函数,因为它所描述的是每一帧之间动画速度的函数。
接下来我们给使用关键帧改变图层颜色的动画每次改变都加上脉冲的效果,代码如下:
override func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent?) {let keyFrameAnimation = CAKeyframeAnimation()keyFrameAnimation.keyPath = "backgroundColor"keyFrameAnimation.duration = 1.0// 五个颜色keyFrameAnimation.values = [UIColor.red.cgColor,UIColor.green.cgColor,UIColor.blue.cgColor,UIColor.yellow.cgColor,UIColor.orange.cgColor]// 五个时间点keyFrameAnimation.keyTimes = [0.0,0.2,0.4,0.6,0.8,1.0]// 缓冲keyFrameAnimation.timingFunctions = [CAMediaTimingFunction(name: CAMediaTimingFunctionName.easeIn),CAMediaTimingFunction(name: CAMediaTimingFunctionName.easeIn),CAMediaTimingFunction(name: CAMediaTimingFunctionName.easeIn),CAMediaTimingFunction(name: CAMediaTimingFunctionName.easeIn)]colorLayer.add(keyFrameAnimation, forKey: nil)}
效果如下:
结语
本篇博客我们介绍了图层动画和视图动画的缓冲函数,列举了一些常用的缓冲函数及其使用效果,最后我们讨论了关键帧动画缓存函数的设置方案。
不过对于动画的缓冲来说,这仅仅是一个开始,后面的博客我们将开始更加深入的探讨CAMediaTimingFunction是如何工作的,并且我们该如何自定义一个更适合自己动画的缓冲函数。