先来个效果图吧:

这是MMTweenAnimation库实现的一个弹跳动画。MMTweenAnimation基于Facebook的pop动画库,它提供了10套自定义的动画曲线,分别是:Back、Bounce、Circ、Cubic、Elastic、Expo、Quad、Quart、Quint、Sine。具体的效果可以参考MMTweenAnimation。
在这里,我们主要来MMTweenAnimation的具体实现及使用。
我们知道,动画实际上是许多帧静止的画面,以一定的速度连续播放,由于肉眼视觉残象产生的错觉,因此我们感觉画面是活动的。这就是动画的基本原理。所以,我们要做的就是按一定的速率去播放帧,在每一帧中计算曲线的路径,并将其绘制到界面上。这主要涉及到曲线的插值算法。
主要部件
MMTweenAnimation的主体类主要有两个:MMTweenAnimation和MMTweenFunction。MMTweenFunction类主要定义各种插值算法,MMTweenAnimation主要是实现动画操作。
MMTweenFunction类
MMTweenFunction类主要是实现各种插值算法。这些插值算法分别10类,即上面列出的10套动画。而每套根据不同的缓动方式,又分为EaseIn、EaseOut、EaseInOut三种,因此MMTweenAnimation库实际上是实现了30种动画。每个插值算法都实现为一个闭包函数,其定义如下:
|
|
而每个动画的插值都是根据数学公式算法出来的,我们以图例中的Bounce-EaseOut动画为例,其实现如下:
|
|
计算出来的插值将会用于计算当前帧的终点值。
MMTweenAnimation类
MMTweenAnimation是实现动画的主体类。这个类继承自pop的POPCustomAnimation,POPCustomAnimation 直接继承自PopAnimation类,用于创建自定义动画的基类,它基本上是一个 display link的方便的转换,来在动画的每一个tick的回调block中驱动自定义的动画。
MMTweenAnimation定义了几个基本属性,如下所示:
|
|
而MMTweenAnimation类最关键的是定义它的回调block。MMTweenAnimation类定义了一个类方法animation(),在这个方法中,通过调用从父类继承来的便捷初始化方法
|
|
来创建一个MMTweenAnimation对象。其实现如下所示:
|
|
其中动画回调的定义如下:
|
|
以上两个类便是MMTweenAnimation的主要部件。
动画示例
有了主要部件,我们就来看看怎么去使用它。MMTweenAnimation给了一个示例,其效果就是开头的图例。为此,MMTweenAnimation定义了类MMPaintView,这个类的主要目的就是绘制上面的曲线,其主要操作如下:
|
|
这个方法首先是将参数中的点(即每一帧计算出来的终点值)添加到对象的__dots数组中,然后再通过__interpolateCGPointsWithCatmullRom方法创建一条Bezier曲线,最后调用setNeedsDisplay()来重新绘制曲线。
我们先来看看这个点是如何获取到的。在MMAnimationController类,我们定义动画对象时,设置了其动画回调,如下所示:
|
|
这个动画回调获取当前时间结束点的值,用于设置小红点的中心位置,同时将这个中心位置的值丢给MMPaintView对象去生成Bezier曲线。
动画渲染操作执行的时间点
知道了MMTweenAnimation库的主要部件,我们现在来看看动画是如何被驱动的。我们在MMTweenAnimation类的animation()方法中,在动画回调的起始位置打个断点,运行一下程序,看看调用栈,如下所示:

可以看到在Run Loop中执行了一个观察者回调,在这个回调中调用了POPAnimator对象的_scheduleProcessPendingList方法的一个block回调,一直追溯到我们的动画操作。也就是说,是在Run Loop的某个时刻执行了一次动画的渲染。
我们再从代码入手,来看看动画执行代码是什么时候添加到Run Loop中的。在MMAnimationController的viewDidAppear方法中,有如下调用:
|
|
其中pop_addAnimation方法是POPAnimator类中定义的。顺着代码,我们最终可以找到_scheduleProcessPendingList的定义,其实现如下:
|
|
可以看到在这个方法中创建了一个Run Loop的观察者,这个观察者在Run Loop的kCFRunLoopBeforeWaiting或kCFRunLoopExit阶段时会执行监听回调处理函数。回调函数中调用了_processPendingList方法,然后从调用栈里面可以看到,一直会执行到MMTweenAnimation的动画闭包中,即我们打断点的地方。
OK,动画渲染时间点找着了,那整个流程就可以完整拼接起来了。
小结
MMTweenAnimation的实现并不复杂,只要了解了动画的基本原理和其中的插值算法,再加上一些pop动画的基础知识,基本上就OK了。要想做出很牛B的动画,还是需要大量的数学知识。其实在MMTweenAnimation库中,除了那10套插值算法外,在MMPaintView类中,计算Bezier的控制点时,还用到了Catmull-Rom样条与Hermite样条,大家有兴趣可以研究一下。
MMTweenAnimation初始源码是Objective-C实现的,我将它用Swift重写了一遍,并放在github上,地址是MMTweenAnimation-Swift,有兴趣可以看一下。
本想放在知识小集中,但由于篇幅稍长,所以独立成篇。