源码阅读分析 (深入浅出FaceBook POP动画引擎)

  这篇文章会分两部分写:第一部分学习分析FaceBook POP动画引擎的框架及值得学习的点。第二部分会简化它实现一个简化版让大家更加容易理解这个框架的思想,看了第一部分还不能读懂POP里面源码,那可以先看看第二部分我提供的Demo,也许更容易理解个大概。注:写这个的时候才出到swift 2.3,所以如果要运行本文的demo可以使用xcode 7.x版,如果使用xcode 8.x,还需要做swift 3.0的转换工作。


 

POP动画引擎

 首先打开POP的工程,我们可以看到它的目录结构如下,接下来就会说一下相关要点:

Animations目录:提供了动所有的动画。

1、POPAnimation 抽象基类定义了动画的名字,开始时间,重复次数,是否自动倒退等,既可以使用delegate的方式来监听动画的状态,也支持使用block的方式。它是个抽象类,不是被直接使用的。POPAnimationState 是一个结构体,它真正保存了POPAnimation要设置的一些属性值、数据等。POPAnimation相当于只提供对外的接口。 POP里面每个子类Animation都有一个State与它一一对应。

2、POP的小技巧之一 FB_PROPERTY_GET与NE_RW_PROPERTY_OBJ_COPY这些宏是重写POPAnimation的get和set方法,使得能够很方便的把值设置到POPAnimationState的成员变量中。

 

Engine目录:看名字就知道,它是引擎的意思。

1、里面比较核心的类有 POPAnimatableProperty、 POPStaticAnimatablePropertyState、POPAnimator、POPAnimationRuntime文件内的C方法。

2、POPStaticAnimatablePropertyState对应每个要动画属性的读写Block函数,属性名,并且有一个静态表 _staticStates。它保存了所有layer相关的所有可动画属性的POPStaticAnimatablePropertyState。

3、POPAnimatableProperty分为不可变和可变的两类,分别是POPAnimatableProperty、POPMutableAnimatableProperty。在POPAnimatableProperty中会用到
+ (id)propertyWithName:(NSString *)name;

这个方法主要是用来构建实例,它返回的是POPStaticAnimatableProperty,它有个POPStaticAnimatablePropertyState类形的成员变量,即上面所说到的所有静态表 _staticStates中某一项。

POPMutableAnimatableProperty中构建实例一般会使用下面这个方法,有人可能会疑惑,对于可变版本不能用两段式构造alloc init出来,然后设置对应的读写block或其它值吗?答案是可以的

+ (id)propertyWithName:(NSString *)name initializer:(void (^)(POPMutableAnimatableProperty *prop))block;

但注意这两个方法中都会有个name,它内部帮我们做了个简单的缓存机制。

4、POPAnimationRuntime主要写了很多的C函数。利用runtime得知要进行动画的属性的类型,从而能够使它能用自己的Vector向量知道如何POPBOX、POPUnbox,实现了单一数据结构就能对于CALayer所有可动画属性时行相互转换。转换之后数学方面的计算就是可以简单的对Vector里的每个值进行数学方面的运算。这样代码接口就是统一了,这是它的另一个亮点。还有一个比较隐藏的点,什么时候去获取属性的类型呢,就是在设置动画的toValue的时候。

5、POPAnimator这个可以说是核心,桥梁。它里面有个CADisplayLink,整个动画引擎就是靠CADisplayLink的回调驱动计算以及更新的。

Utility目录:工具

1、ActionDisabler:利用类的构造和析构函数,成对使用禁用和开启动画的函数。

2、POPMath:动画曲线相关,如系统的CAMediaTimingFunction,它对于我们是透明的,但这里我们可以实实在在看得到实现的。有一点很重要就是我们只要知道输入一个时间值,根据它的CAMediaTimingFunction或其它的曲线如要实现Decay的动画的曲线,就会以得到一个结果值。

3、剩下的就不一一介始了,都是数学相关的,我们重点是了解整个流程。

主要类图如下,并没有列出所有

可以再回头看看上面的说明。

 

更新一帧的流程如下:POPAnimator的displayLink回调了->遍历所有的POPAnimatorItem->拿到POPAnimatorItem中的POPAnimation、POPAnimationState、及动画的载体obj(这里没有画出来,一般就是CALayer.当你调用layer pop_addAnimation方法时,这个layer就被POPAnimatorItem弱引用)->根据当前时间调用POPAnimationState的advanced方法来计算结果(这里就会用到Uitility里面的数学方法的函数)->如果计算到的数值有更新也是保存在POPAnimationState的currentVec向量中->接下来POPAnimator就会调用updateAnimatable方法把currentVec值更新到动画载体中(此时POPAnimatableProperty就派上用场了,它提供了相应数据类型与vector间的转换)

更多细节就不再写,源码都有。

 


  自己动手实现POP SimplePOP引擎

既然都读懂了他里面的原理,自己就动手实现一个吧,在这个Demo省略了很多东西,它只是个原型,并没有提供很多功能,只提供了几个简单的曲线的动画,它还有很多BUG,没有关系,只是为了简化,也不是想完全实现POP的功能。但它完全模枋了POP的结构,是代码是用swift写的。希望能把POP引擎的思想剥离出来。github: SimplePOP

用法接口都做得跟POP一样的,创建运行一个动画如下:

       let animation : SimplePOPBasicAnimation = SimplePOPBasicAnimation.animationWithPropertyNamed(kSimplePOPPosition)

            animation.formValue = self.animationView.layer.position

            var from = animation.formValue

            NSLog("%@, fromValue: %@", NSStringFromCGPoint(from!), #function)

            from!.y += 200

            animation.toValue = from

            animation.timingFunctionType = SimplePOPTimingFunctionType.BounceEaseOut            

            NSLog("%@, toValue: %@", NSStringFromCGPoint(from!), #function)

            self.animationView.layer.simplePOP_addAnimation(animation, withKey:"TextKey")

目前只实现了一个位置相关的动画,希望你能够下载demo下来玩玩一下,里实现了一个球掉地后反弹到静止的过程。希望这个能增加你对这个引警的兴趣,里面实现都基于一个定时器CADisplayLink驱动。学习完POP可以了解到如果要自己实现一个基于定器的动画引擎,应该如何设计模块间的功能,职责。如何运用算法现有的曲线算法到你的引擎中。


参考:https://github.com/facebook/pop

   https://github.com/AttackOnDobby/iOS-Core-Animation-Advanced-Techniques

 

 

 

 

 

 

posted on 2016-08-30 14:05  chenxianming  阅读(1015)  评论(0编辑  收藏  举报

导航