您的位置:首页 > 编程语言

Facebook Pop 代码解析及使用指南(部分代码参考第三方研究及官方定义)

2014-09-20 17:14 525 查看

FacebookPop研究


研究人:*******
9月中旬
2014年秋季

/*该篇文章借鉴了许多前辈精心之作,因被借鉴文章太多,在此不一一声明了,如果有前辈看到自己被引用的部分可以联系添加备注,在此不胜感激*/

Facebook Pop研究
第一部分

Facebook Pop框架结构

何为Facebook Pop?

Pop是一个可扩展的iOS和OS X。除了基本的静态动画引擎,它支



持弹簧和衰减动态动画,使它有助于建立现实的,基于物理的相互作用。



该API允许快速整合现有的Objective-C代码库和任何对象任何属性的动



画。这是一个成熟和完善的测试框架,驱动所有的动画和过渡纸。(官方



定义)通俗的讲是开源的动画实现建构



Pop动画形式:



POPAnimationState
纪录动画状态

{

kPOPAnimationSpring,//弹簧效果动画

kPOPAnimationDecay,//衰减效果动画

kPOPAnimationBasic,//贝塞尔曲线运动效果动画

kPOPAnimationCustom,//自定义效果动画

}





POP的架构



POP目前由四部分组成:1.Animations;2.Engine;3.Utility;4.WebCore。



Animations: 定义pop支持的动画类型,并抽像各种动画的低层数据结构



Engine:组织动画运行的数据结构,核心是动化管理者,还包括了动画引擎所需要的可动画属性定义、动画追踪等内容



Utility: 封装Engine中用到的各种功能,包括数值运算,数据转换,运算等



WebCore: 这部分是苹果公司的代码,矩阵运算和贝赛尔曲线的,用于底层运算。



通过 Engine、Utility、WebCore三个基石,打造了Animations



为何用Pop?

POP动画极为流畅,Engine中的POPAnimator类使用CADisplayLink,

而CADisplayLink高达 60 FPS的特性,打造了一个游戏级的动画引擎。

通过CADisplayLink,Apple允许 App
的重绘速度设定到和屏幕刷新频率一致,由此可以获得非常流畅的交互动画.Pop中两个很好的地方:

1.除了基本的动画外,Pop还提供spring和decay动画;

2.你可以使用Pop对NSObject对象上的任何属性执行动画效果,而不仅仅是UIKit中声明的动画属性。





第二部分

——Pop代码阅读解析

Pop代码阅读解析:



注释:每个类有三个文件或者一到两个不等.在三个文件中private.h声明该类中所用变量,.h文件中定义属性,.mm文件具体实现.(此规律适合该代码的绝大部分类)



一.从POPAnimator深入解读Pop架构:

POPAnimator是整个Pop动画的管理者,在POPAnimator中定义了所有的动画事件(example:POPAnimatorItem,纪录了动画作用到的对象、动画key、动画对象等信息),提供了add、remove等操作Animation集合的方法,并提供了Observer和delegate等事件函数根据key为id添加动画集合的方法,以及移除全部动画集合或者是指定动画集合.此外还有为object添加动画关键值的数组调用方法,为动画add/remove
观察者的方法



1.PopAnimation:



定义了event数组,用数组来接收event,以及各种类型动画的公共属性和方法,是各种动画的父类,以下是Animation中用到的类

(1)POPGeometry:NSValue和Pop自定义类型的转换



(2)POPAnimationTracer:动画跟踪

<1>POPAnimationEvent:

定义结构体POPAnimationEventType声明POPAnimationTracer中的各个属性

<2>POPAnimationTracerInternal:

定义了initWithType:(POPAnimationEventType)方法来初始化event



(3)POPAnimationTracerInternal:

在该类中声明了read/write value值属性的函数,以及更新 ToValue,FromValue,Velocity,speed,mass等属性的函数



(4)POPAnimationRuntime:

包装函数vector,把vector转换成point,size,rect,color等,还有逆过程,即将point,size,rect,color封装成vector



功能类有:

(5)POPAction:

定义了ActionDisabler和ActionEnabler ,创建ActionEnabler时停止动画,销毁时重新运行



(6)POPSpringSolver:进行插值运算



2.POPAnimationExtras:

扩展CAAnimation 和POPSpringAnimation方法,用到的类

用到的功能类有:

(1)POPMath:

该类用于数学计算,封装了UnitBezier,POPVector和其他数字计算其中POPVector是子类化的一个类,UnitBezier是用iOS自带的计算贝塞尔曲线方法确定运动路径

<1>POPVector :该类用于向量计算

<2>UnitBezier:结构体,初始化参数为两个控制点p1(p1x,p1y), p2(p2x,p2y),用于表示起始点为s(0,0),终止点为e(1,1),p1为第一控制点,p2为第二控制点的二次Bezier。

用途:重新实现系统的自带动画



//以下类只做简单的介绍,在应用指南中详解



3.POPBasicAnimation:(继承自POPPropertyAnimation)

从POPAnimation中子类化的类,该类具有基本动画的一切属性及方法,是pop四大核心之一



4.POPDecayAnimation:(继承自POPPropertyAnimation)

该类用来实现减速效果,有velocity速率属性,以及deceleration方向,duration持续时间





三.POPPropertyAnimation&POPCustomAnimation:

POPPropertyAnimation&POPCustomAnimation是POPAnimation子类化出来的两个类,POPPropertyAnimation的.mm文件中重写了set方法,在POPPropertyAnimation中使用了以下类



1.POPAnimatableProperty:

用于定义动画类型和属性,以及修改值.它定义了Layer,View,ScrollView,TableView,CollectionView,NavigationBar,ToolBar,TabBar,Label的动画属性,利用POP可以直接给控件添加动画



POPAnimatableProperty中的类族有:

POPConcreteAnimatableProperty

POPMutableAnimatableProperty

POPPlaceholderAnimatableProperty

POPStaticAnimatableProperty

设略到的功能类有:

(1)POPCGUtils:颜色转换函数,数组和点,point,size,rect,color等转换函数

(2)POPLayerExtras:图层矩阵变换,底层由TransformationMatrix实现

<1>TransformationMatrix:矩阵运算,初始化scal,rotate,translate,flipx,skew,applyPerspection等属性

<2>FloatConversion:



浮点数转换方法,提供double->float和double->CGFloat,

用途:服务于矩阵运算



总的来说,能够为开发者所用的仅有以下四个类



POPCustomAnimation

POPBasicAnimation

POPDecayAnimation

POPSpringAnimation

正是这四个类提供了强大的动画功能



//以下是从他人微博中的运行介绍,因本人不会组织语言,所以在此借用

2.动画执行流程:

1)POPAnimator是单例对象,初始化对象实例时注册把CADisplayLink到runloop,定时调用render,通过renderTime函数遍历所有正在执行的动画,使动画进行到下一帧,并刷新画面,直到所有动画推进完成。

2)动画状态的推进最终都是通过_POPAnimationState类和其子类的advance方法推进的,advance更新时间戳,updateAnimatable方法把state的状态变更到动画要作用的obj对象。

3)Spring、Decay、Basic都有各自实现的advance函数计算,而Custom方式不同,他是通过一个POPCustomAnimationBlock类型的回调来就算这一帧的值的。



3.动画算法

不同动画的advance都是如何推进的呢?

Basic:动画通过起点为(0,0),终点为(1,1)的Bezier曲线计算,x轴为时间,y轴为比例。advance方法中需要根据t值计算出对应的y值p,然后根据p的值进行插值计算(v=s+(e-s)*p),并进行了按照最小精度的四舍五入(源码设置的精度是1/(1000*总时间),动画时间越长,精度越高)。



Spring:这里的Vector4d不是真的向量,它可以表示point,size等结构,它起始就是个属性的数组而已,不要去想它的几何意义。

这个模型其实是一个简谐运动,

计算过程类似于弹簧,计算主要使用到这样几个量:

|---------|------|------轨迹

起点 当前点 间隔 平衡位置

p,表示当前点到弹簧平衡位置的距离 v,表示振子的速度 当前时间t 间隔时间dt

k弹簧的劲度系数 f= kl b阻力系数 m质量

_tp 瞬时值(偏移量)

_tv 瞬时速度

_ta 瞬时加速的



Derivative 导数~~

利用这些参数做插值算法,算出下一时刻的p和v

计算原理大致就是p2= p+ dt * v(因为v=p');v2
= v + dt * v'。

计算过程中运用了插值算法减小误差。说实话,没太看懂。希望这方面专家补充下。

问题:程序中计算dv用的是state.p*(-_k/_m)- state.v*(_b/_m);

正常dv=加速度a=f/m=f牵引/m-f阻力/m
= state.p*(-_k/_m) - 阻力/m 这里库的作者可能认为阻力和速度成正比~~~,高中物理学的阻力是恒定的,但是模型运行的效果确实不错。



Decay:衰减函数,没有说明不知道为什么,应该和衰减公式有点关系,没弄清楚怎么变形的,下边是源码中的注释,v表示速度,dt时间间隔x位移

// v0 = v / 1000

// v = v0 * powf(deceleration, dt);

// v = v * 1000;

// x0 = x;

// x = x0 + v0 * deceleration * (1 - powf(deceleration, dt)) / (1 -deceleration)



Custom:通过自定义block刷新动画帧,更灵活的方式

第三部分
——Facebook POP使用

//以下部分参考官方指南

基本类型



SpringAnimation

ease-inease-out 这些可能你已经非常熟悉,这是动画的动作标配了,不过POP觉得只是这样显然太无聊,提供了两个非常不同于其的动画模式,第一个就是Spring
Animation。


SpringAnimation 由诸多的复杂参数来控制,展现了一个非常风骚的姿势。

1.springBounciness弹簧弹力取值范围为[0,20],默认值为4

2.springSpeed弹簧速度,速度越快,动画时间越短[0, 20],默认为12,和springBounciness一起决定着弹簧动画的效果

3.dynamicsTension弹簧的张力

4.dynamicsFriction弹簧摩擦

5.dynamicsMass质量。张力,摩擦,质量这三者可以从更细的粒度上替代springBounciness和springSpeed控制弹簧动画的效果



Tension,Friction,Mass这三个参数的作用很微妙,需要你在示例程序里去仔细体会。



使用 Spring Animation的方式非常简单。

1. POPSpringAnimation *anim = [POPSpringAnimation animationWithPropertyNamed:kPOPLayerScaleXY];

2. anim.toValue = [NSValue valueWithCGPoint:CGPointMake(2.0, 2.0)];

3. anim.springBounciness = 4.0;

4. anim.springSpeed = 12.0;

5. anim.completionBlock = ^(POPAnimation *anim, BOOL finished) {

6. if (finished) {NSLog(@"Animation finished!");}};

通过 [POPSpringAnimation animationWithPropertyNamed:kPOPLayerScaleXY]我们创建了一个二维平面上分别沿着 X和
Y 坐标轴进行缩放的动画。



因此我们要使用 toValue来告诉POP
我们希望分别缩放几倍,如果你不提供fromValue,那么POP将默认从当前的大小为依据进行缩放。值得一提的是,toValue这里的值要和动画作用的属性一样的结构。如果我们操作bounds,那么这里应该是[NSValue
valueWithCGRect:CGRectMake(0.0, 0.0, 200.0,400.0)]。



completionBlock提供了一个Callback,动画的执行过程会不断调用这个block,finished这个布尔变量可以用来做动画完成与否的判断。



最后我们使用 pop_addAnimation来让动画开始生效,如果你想删除动画的话,那么你需要调用pop_removeAllAnimations。与
iOS自带的动画不同,如果你在动画的执行过程中删除了物体的动画,那么物体会停在动画状态的最后一个瞬间,而不是闪回开始前的状态。



DecayAnimation

DecayAnimation 就是POP提供的另外一个非常特别的动画,他实现了一个衰减的效果。这个动画有一个重要的参数velocity(速率),一般并不用于物体的自发动画,而是与用户的交互共生。这个和iOS7引入的UIDynamic非常相似,如果你想实现一些物理效果,这个也是非常不错的选择。



Decay的动画没有toValue只有fromValue,然后按照velocity来做衰减操作。如果我们想做一个刹车效果,那么应该是这样的。

1. POPDecayAnimation *anim = [POPDecayAnimation animWithPropertyNamed:kPOPLayerPositionX];

2. anim.velocity = @(100.0);

3. anim.fromValue = @(25.0);

4. //anim.deceleration = 0.998;

5. anim.completionBlock = ^(POPAnimation *anim, BOOL finished) {

6. if (finished) {NSLog(@"Stop!");}};

这个动画会使得物体从 X坐标的点25.0
开始按照速率100点/s做减速运动。这里非常值得一提的是,velocity也是必须和你操作的属性有相同的结构,如果你操作的是bounds,想实现一个水滴滴到桌面的扩散效果,那么应该是[NSValue
valueWithCGRect:CGRectMake(0, 0,20.0, 20.0)]



如果 velocity是负值,那么就会反向递减。



deceleration(负加速度)是一个你会很少用到的值,默认是就是我们地球的0.998,如果你开发给火星人用,那么这个值你使用0.376会更合适。



PropertyAnimation & Basic Animation

POP号称可以对物体的任何属性进行动画,其背后就是这个Property Animation驱动。SpringAnimation和Decay
Animation都是继承自这个类,接下来我们通过一个Counting Label的例子来展现这个神奇的能力。

与此同时我们也使用了 Basic Animation,经典的ease-in-out此刻发挥了重要的作用,因为我们并不需要计数器的数值进行回弹。

1. POPBasicAnimation *anim = [POPBasicAnimation animation];

2. anim.duration = 10.0;

3. anim.timingFunction = [CAMediaTimingFunction functionWithName:kCAMediaTimingFunctionEaseInEaseOut];

4. POPAnimatableProperty * prop = [POPAnimatableProperty propertyWithName:@"count" initializer:^(POPMutableAnimatableProperty *prop) { prop.readBlock = ^(id obj, CGFloat values[]) {

5. values[0] = [[obj description] floatValue];};

6. prop.writeBlock = ^(id obj, const CGFloat values[]) {

7. [obj setText:[NSString stringWithFormat:@"%.2f",values[0]]];};

8. prop.threshold = 0.01;}];

9. anim.property = prop;

10. anim.fromValue = @(0.0);

11. anim.toValue = @(100.0);

POPBasicAnimation的 timingFunction我们定义了动画的方式,慢开慢停。随后通过POPAnimatableProperty定义了POP
如何操作Label
上的数值。



readBlock中,obj就是我们的Label,values这个是动画作用的属性数组,其值必须是CGFloat,之前我们在Decay
Animation中操作了bounds



那么 values[0],values1,values2,values3就分别对应CGRectMake(0,
0, 20.0, 20.0)的0, 0, 20.0, 20.0



这里我们只需要操作 Label上显示的文字,所以只需要一个参数。通过values[0] = [[obj description] floatValue]我们告诉
POP 如何获取这个值。



相应的我们通过 [obj setText:[NSStringstringWithFormat:@”%.2f”,values[0]]]告诉了 POP
如何改变Label
的属性。



threshold定义了动画的变化阀值,如果这里使用 1,那么我们就不会看到动画执行时候小数点后面的数字变化。到此为止,我们的Counting
Label 就完成了,是不是超简单?



实战



PopUp& Decay Move

这个实例中我们介绍下如何将 Decay动画和用户的操作结合起来,实现一个推冰壶的效果。



首先我们给我们的物体添加个 UIPanGestureRecognizer的手势操作其,处理方式如下

1. case UIGestureRecognizerStateChanged: {

2. [self.popCircle.layer pop_removeAllAnimations];

3. CGPoint translation = [pan translationInView:self.view];

4. CGPoint center = self.popCircle.center;

5. center.x += translation.x;

6. center.y += translation.y;

7. self.popCircle.center = center;

8. [pan setTranslation:CGPointZero inView:self.popCircle];

9. break;

10. }

11. case UIGestureRecognizerStateEnded:

12. case UIGestureRecognizerStateCancelled: {

13. CGPoint velocity = [pan velocityInView:self.view];

14. [self addDecayPositionAnimationWithVelocity:velocity];

15. break;

16. }

当用户触摸这个冰壶的时候,所有动画会立刻停止,然后跟随用户的手指移动。通过 [panvelocityInView:self.view];我们获取了用户手指移动的速率然后在addDecayPositionAnimationWithVelocity中处理动画

1. POPDecayAnimation *anim = [POPDecayAnimation animationWithPropertyNamed:kPOPLayerPosition];

2. anim.velocity = [NSValue valueWithCGPoint:CGPointMake(velocity.x, velocity.y)];

当用户松开手之后,冰壶会依照地球的重力在低摩擦的状态下前进逐渐停止。如果想增大摩擦力,你可以把速率乘以一个摩擦系数。



FlyIn

在这个实例中,我们介绍下如何结合两个动画。实现一个像Path的卡片飞入的效果。


同样保留了 Decay Move的效果,你可以甩走这张卡片。

1. POPSpringAnimation *anim = [POPSpringAnimation animationWithPropertyNamed:kPOPLayerPositionY];

2. anim.fromValue = @-200;

3. anim.toValue = @(self.view.center.y);

4.

5. POPBasicAnimation *opacityAnim = [POPBasicAnimation animationWithPropertyNamed:kPOPLayerOpacity];

6. opacityAnim.timingFunction = [CAMediaTimingFunction functionWithName:kCAMediaTimingFunctionEaseInEaseOut];

7. opacityAnim.toValue = @1.0;

8.

9. POPBasicAnimation *rotationAnim = [POPBasicAnimation animationWithPropertyNamed:kPOPLayerRotation];

10. rotationAnim.timingFunction = [CAMediaTimingFunction functionWithName:kCAMediaTimingFunctionEaseInEaseOut];

11. rotationAnim.beginTime = CACurrentMediaTime() + 0.1;

12. rotationAnim.toValue = @(0);

首先把我们的冰壶变成卡片,旋转一点角度。这里需要注意的是,我们使用了duration来定义了Basic Animation的执行时间,beginTime来定义了动画的开始时间。beginTime接受的是一个以秒为单位的时间,所以我们使用了CACurrentMediaTime()获取了当前时间,然后加上了延迟时间。



Transform

这个实例是真的酷极了的效果,我们将实现一个用户点击后播放按钮转换为进度条容器的变形效果。

首先我们创建一个进度条,这个真是我最拿手的事情了。通过lineCap lineWidth我们调整进度条的样式,然后使用UIBezierPath定义了进度条的走向。

1. CAShapeLayer *progressLayer = [CAShapeLayer layer];

2. progressLayer.strokeColor = [UIColor colorWithWhite:1.0 alpha:0.98].CGColor;

3. progressLayer.lineWidth = 26.0;

4.

5. UIBezierPath *progressline = [UIBezierPath bezierPath];

6. [progressline moveToPoint:CGPointMake(25.0, 25.0)];

7. [progressline addLineToPoint:CGPointMake(700.0, 25.0)];

8. progressLayer.path = progressline.CGPath;

9.

10.

11. POPSpringAnimation *scaleAnim = [POPSpringAnimation animationWithPropertyNamed:kPOPLayerScaleXY];

12. scaleAnim.toValue = [NSValue valueWithCGPoint:CGPointMake(0.3, 0.3)];

13.

14. POPSpringAnimation *boundsAnim = [POPSpringAnimation animationWithPropertyNamed:kPOPLayerBounds];

15. boundsAnim.toValue = [NSValue valueWithCGRect:CGRectMake(0, 0, 800, 50)];

16. boundsAnim.completionBlock = ^(POPAnimation *anim, BOOL finished) {

17. if (finished) {

18. UIGraphicsBeginImageContextWithOptions(self.popCircle.frame.size, NO, 0.0);

19. POPBasicAnimation *progressBoundsAnim = [POPBasicAnimation animationWithPropertyNamed:kPOPShapeLayerStrokeEnd];

20. progressBoundsAnim.timingFunction = [CAMediaTimingFunction functionWithName:kCAMediaTimingFunctionEaseInEaseOut];

21. progressBoundsAnim.toValue = @1.0;

22. progressBoundsAnim.completionBlock = ^(POPAnimation *anim, BOOL finished) {if (finished) {UIGraphicsEndImageContext();}};

23. [progressLayer pop_addAnimation:progressBoundsAnim forKey:@"AnimateBounds"];

24. }

25. };

首先是一起进行的 scale和 bounds
的变化效果,播放按钮将缩小然后改变外形成为进度条的容器,在变形结束后,我们触发进度条的动画。



这里我们使用UIGraphicsBeginImageContextWithOptions(self.popCircle.frame.size, NO, 0.0);开启了绘画上下文,动画结束后使用UIGraphicsEndImageContext();清空了绘画上下文。这个主要是影响了画板的大小。



这里我们没有使用UIGraphicsBeginImageContext()而是使用UIGraphicsBeginImageContextWithOptions()以此获取一个更清晰的绘图效果。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: