iOS 开发之使用 Facebook POP

需要打造游戏级别的动画效果?

Facebook 开源的 POP 是一个在 iOS 与 OS X 上通用的极具扩展性的动画引擎,它在基本的静态动画的基础上增加的弹簧动画与衰减动画。

POP 通过 CADisplayLink 将 APP 的重绘速度提高到跟屏幕刷新频率一致的 60 FPS !从而提供游戏级别的动画引擎,由此我们可以创造出更真实、更具物理性、更流畅的交互动画!

没有对比就没有伤害,感兴趣的的同学可以看一下 纯洁的小袋子
的“ Core Animation & Facebook's POP 对比” 。

POP 的架构

POP 目前由四部分组成:

  • Animations
  • Engine
  • Utility
  • WebCore
Facebook POP

WebCore 里包含了一些从 Apple 的开源的网页渲染引擎里拿出的源文件,与 Utility 里的组件一并,提供了 POP 的各项复杂计算的基本支持。

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

POP 提供的动画类

  • POPAnimation(动画的抽象基类)
  • POPBasicAnimation (基本动画类)
  • POPSpringAnimation (弹性动画类)
  • POPDecayAnimation (衰减动画类)
  • POPCustomAnimation (自定义动画类)
  • POPAnimatableProperty (自定义属性动画)
  • POPPropertyAnimation(自定义属性动画)

动画的抽象基类 POPAnimation

#import 

#import 
#import 

@class CAMediaTimingFunction;

/**
 动画的抽象基类.
 */
@interface POPAnimation : NSObject

/**
 动画的名称
 根据这个属性用来区别动画;识别动画
 */
@property (copy, nonatomic) NSString *name;

/**
 动画的开始时间;
 默认是从0开始启动
 */
@property (assign, nonatomic) CFTimeInterval beginTime;

/**
 动画的 delegate
 详情查看查看[POPAnimationDelegate]
 */
@property (weak, nonatomic) id delegate;

/**
 动画的追踪器
 记录所有动画相关事件,还允许完成后对其进行查询和分析;更多可以查看 [POPAnimationTracer.h]
 */
@property (readonly, nonatomic) POPAnimationTracer *tracer;

/**
 动画开始的时候回调的block
 */
@property (copy, nonatomic) void (^animationDidStartBlock)(POPAnimation *anim);

/**
 动画达到toValue或者超过值的时候调用的block
 */
@property (copy, nonatomic) void (^animationDidReachToValueBlock)(POPAnimation *anim);

/**
 动画完成的时候调用的block
 */
@property (copy, nonatomic) void (^completionBlock)(POPAnimation *anim, BOOL finished);

/**
 正在做动画的时候调用;调用次数比较多
 */
@property (copy, nonatomic) void (^animationDidApplyBlock)(POPAnimation *anim);

/**
 完成动画的时候是否删除动画;
 默认为YES;
 设置NO的话
 */
@property (assign, nonatomic) BOOL removedOnCompletion;

/**
 动画是否已暂停;
 在初始化的时候,默认YES;在动画添加的时候,隐式暂停???在动画完成的时候和 removedOnCompletion = NO 的时候,动画是暂停的;
 */
@property (assign, nonatomic, getter = isPaused) BOOL paused;

/**
 动画是否逆转;比如向前的动画,做完之后,会再后退回来;
 注意:时间是原来的 2 倍,动画到 toValue 后,又回到原始的值;
 delegate 跟再做一次动画一样;
*/
@property (assign, nonatomic) BOOL autoreverses;

/**
 重复动画次数;
 = 0 或者 1 不会重复;
 注意:
 delegate 中
 animationDidStart:每次动画重复开头调用;
 animationDidReachToValue:每次到 toValue 的时候调用;
 animationDidStop:finished:每次到 toValue 的时候调用,如果设置了 autoreverses,动画还未完成,返回 NO;
 设置了 autoreverses,动画时间是原来 2 倍;
 */
@property (assign, nonatomic) NSInteger repeatCount;

/**
 一直重复做动画;
 delegate 中 animationDidStop 将恒等于 NO;
 */
@property (assign, nonatomic) BOOL repeatForever;

@end


@protocol POPAnimationDelegate 
@optional

/**
 动画开始的时候调用
 */
- (void)pop_animationDidStart:(POPAnimation *)anim;

/**
 动画达到toValue或者超过的时候调用;
 */
- (void)pop_animationDidReachToValue:(POPAnimation *)anim;

/**
 动画停止
 */
- (void)pop_animationDidStop:(POPAnimation *)anim finished:(BOOL)finished;

/**
 正在做动画的时候调用;
 */
- (void)pop_animationDidApply:(POPAnimation *)anim;

@end


@interface NSObject (POP)

/**
 添加动画到接收器;
 anim:要添加的动画
 key:动画标识符,可以是任何字符串,但每个动画必须唯一;
 */
- (void)pop_addAnimation:(POPAnimation *)anim forKey:(NSString *)key;

/**
 删除所有附件在接收器上的动画;
 */
- (void)pop_removeAllAnimations;

/**
 删除附加在接收器上的所有关键
 */
- (void)pop_removeAnimationForKey:(NSString *)key;

/**
 返回接收器所有动画的 key 的数组;key 的顺序 = 动画顺序;
 */
- (NSArray *)pop_animationKeys;

/**
 返回某个 key 的动画,= nil 表示不存在
 */
- (id)pop_animationForKey:(NSString *)key;

@end

/**
 实现NSCopying协议;
 */
@interface POPAnimation (NSCopying) 

@end

基本动画类 POPBasicAnimation

#import 

/**
 基础动画
 */
@interface POPBasicAnimation : POPPropertyAnimation

/**
 类创建实例
 */
+ (instancetype)animation;

/**
 指定属性动画;
 */
+ (instancetype)animationWithPropertyNamed:(NSString *)name;

/**
 使用 kCAMediaTimingFunctionDefault 定时功能的基本动画;
 */
+ (instancetype)defaultAnimation;

/**
 @使用 kCAMediaTimingFunctionLinear 定时功能的基本动画;
 */
+ (instancetype)linearAnimation;

/**
 @使用 kCAMediaTimingFunctionEaseIn 定时功能的基本动画;
 */
+ (instancetype)easeInAnimation;

/**
 @使用 kCAMediaTimingFunctionEaseOut 定时功能的基本动画;
 */
+ (instancetype)easeOutAnimation;

/**
 @使用 kCAMediaTimingFunctionEaseIn 定时功能的基本动画;
 */
+ (instancetype)easeInEaseOutAnimation;

/**
 延迟多少秒执行动画:Defaults to 0.4.
 */
@property (assign, nonatomic) CFTimeInterval duration;

/**
 设置动画节奏,默认使用:kCAMediaTimingFunctionDefault

CA_EXTERN NSString * const kCAMediaTimingFunctionLinear
    CA_AVAILABLE_STARTING (10.5, 2.0, 9.0, 2.0);

CA_EXTERN NSString * const kCAMediaTimingFunctionEaseIn
    CA_AVAILABLE_STARTING (10.5, 2.0, 9.0, 2.0);

CA_EXTERN NSString * const kCAMediaTimingFunctionEaseOut
    CA_AVAILABLE_STARTING (10.5, 2.0, 9.0, 2.0);

CA_EXTERN NSString * const kCAMediaTimingFunctionEaseInEaseOut
    CA_AVAILABLE_STARTING (10.5, 2.0, 9.0, 2.0);

CA_EXTERN NSString * const kCAMediaTimingFunctionDefault
    CA_AVAILABLE_STARTING (10.6, 3.0, 9.0, 2.0);
 */
@property (strong, nonatomic) CAMediaTimingFunction *timingFunction;

@end

POPBasicAnimation 提供的四种 TimingFunction

  • kCAMediaTimingFunctionLinear
kCAMediaTimingFunctionLinear
  • kCAMediaTimingFunctionEaseIn
kCAMediaTimingFunctionEaseIn
  • kCAMediaTimingFunctionEaseOut
kCAMediaTimingFunctionEaseOut
  • kCAMediaTimingFunctionEaseInEaseOut
kCAMediaTimingFunctionEaseInEaseOut

示例

POPBasicAnimation * butAnimation = [POPBasicAnimation animationWithPropertyNamed:kPOPViewCenter];
butAnimation.duration = 1.0f;
butAnimation.toValue = [NSValue valueWithCGSize:CGSizeMake(_btn.centerX,_btn.centerY + 400)];
[_btn pop_addAnimation:butAnimation forKey:@"btn_Animation"];

弹性动画类 POPSpringAnimation

#import 

/**
 弹簧动画类;通过弹簧动力学模型实现动画
 */
@interface POPSpringAnimation : POPPropertyAnimation

/**
 初始化一个实例
 */
+ (instancetype)animation;

/**
 指定属性动画的实例
 */
+ (instancetype)animationWithPropertyNamed:(NSString *)name;

/**
 当前速度;
 做动画开始之前应该设置初始速度;
 */
@property (copy, nonatomic) id velocity;

/**
 反弹力度
 范围 [0, 20],默认 4.
 */
@property (assign, nonatomic) CGFloat springBounciness;

/**
 速度
 范围 [0, 20]. 默认 to 12.
 */
@property (assign, nonatomic) CGFloat springSpeed;

/**
 拉力
 影响回弹力度以及速度
 值越大,动画速度越快
*/
@property (assign, nonatomic) CGFloat dynamicsTension;

/**
 摩擦力
 如果开启,动画会不断重复,幅度逐渐削弱,直到停止。
*/
@property (assign, nonatomic) CGFloat dynamicsFriction;

/**
 质量
 细微的影响动画的回弹力度以及速度
*/
@property (assign, nonatomic) CGFloat dynamicsMass;

@end
  • 胡克定义

F=-k·x
表达式为 F=-k·x 或 F=-k·Δx ,弹簧的弹力F和弹簧的伸长量(或压缩量)x 成正比,即 F= k·x 。k 是物质的弹性系数,它只由材料的性质所决定,与其他因素无关。负号表示弹簧所产生的弹力与其伸长(或压缩)的方向相反。

Spring
DECAY

示例

POPSpringAnimation *springAnimaiton = [POPSpringAnimation animationWithPropertyNamed:kPOPLayerPositionY];
springAnimaiton.toValue = @(500);
[_btn pop_addAnimation:springAnimaiton forKey:@"springAnimation"];

springAnimaiton.springBounciness = 20;
springAnimaiton.springSpeed = 20;

以下这 3 个比较难用;如果不是特别需求,springBounciness 和 springSpeed 就可以解决问题;

//  springAnimaiton.dynamicsTension    = _value1;
//  springAnimaiton.dynamicsFriction   = _value2;
//  springAnimaiton.dynamicsMass       = _value3;

衰减动画类 POPDecayAnimation

#import 

/**
 衰减动画,也有称阻尼动画
 */
@interface POPDecayAnimation : POPPropertyAnimation

/**
 实例对象
 */
+ (instancetype)animation;

/**
 指定属性动画的实例
 */
+ (instancetype)animationWithPropertyNamed:(NSString *)name;

/**
 初始速度;
 支持:
 kPOPValuePoint, 
 kPOPValueInteger,
 kPOPValueFloat,
 kPOPValueRect,
 kPOPValueSize;
 */
@property (copy, nonatomic) id velocity;

/**
 原始速度
 用于设置 autoreverse 和 repeatCount
 */
@property (copy, nonatomic, readonly) id originalVelocity;

/**
 减速:较低的值会更快的减速
 范围[0, 1].  默认 0.998.
 */
@property (assign, nonatomic) CGFloat deceleration;

/**
 预计持续时间;
 根据 velocity 和 deceleration 得出
 */
@property (readonly, assign, nonatomic) CFTimeInterval duration;

/**
 基于Velocity 和 deceleration;
 */
- (void)setToValue:(id)toValue NS_UNAVAILABLE;

/**
 反转速度
 */
- (id)reversedVelocity;

@end

示例

POPDecayAnimation *anim = [POPDecayAnimation animationWithPropertyNamed:kPOPLayerPositionY];
anim.velocity = @(300);
[_btn pop_addAnimation:anim forKey:@"slide"];

自定义动画 POPCustomAnimation

#import 

@class POPCustomAnimation;

/**
 是自定义动画的回调块
 每个帧动画回调此 block,最新的属性;
 target:动画对象,避免循环;
 animation:动画实例,确定上次回调来的当前时间,和已经使用的时间;避免循环使用;
 return no = 动画完成;
 */
typedef BOOL (^POPCustomAnimationBlock)(id target, POPCustomAnimation *animation);

/**
 自定义动画
 */
@interface POPCustomAnimation : POPAnimation

/**
 初始化,并返回一个动画实例
 */
+ (instancetype)animationWithBlock:(POPCustomAnimationBlock)block;

/**
 当前动画的时间
 */
@property (readonly, nonatomic) CFTimeInterval currentTime;

/**
 上次回调的时间
 */
@property (readonly, nonatomic) CFTimeInterval elapsedTime;

@end

自定义属性动画 POPAnimatableProperty

#import 
#import 

@class POPMutableAnimatableProperty;
/**
 描述动画属性
 */
@interface POPAnimatableProperty : NSObject 

/**
 根据名字创建动画属性,名字不存在 = nil;
 */
+ (id)propertyWithName:(NSString *)name;

/**
 根据名字创建动画属性,名字不存在 = nil; 如果名字存在,则初始化 block 实例;
 */
+ (id)propertyWithName:(NSString *)name initializer:(void (^)(POPMutableAnimatableProperty *prop))block;

/**
 属性的名字,标识唯一动画属性
 */
@property (readonly, nonatomic, copy) NSString *name;

/**
 返回当前属性值
 */
@property (readonly, nonatomic, copy) void (^readBlock)(id obj, CGFloat values[]);

/**
 修改变化的值
 */
@property (readonly, nonatomic, copy) void (^writeBlock)(id obj, const CGFloat values[]);

/**
 决定动画变化的间隔的阈(yu第四声)值;值越大,writeBlock 的调用次数越少;
 */
@property (readonly, nonatomic, assign) CGFloat threshold;

@end

/**
 可变动画可变属性;
 */
@interface POPMutableAnimatableProperty : POPAnimatableProperty

/**
 属性的名称
 */
@property (readwrite, nonatomic, copy) NSString *name;

/**
 返回当前属性值
 */
@property (readwrite, nonatomic, copy) void (^readBlock)(id obj, CGFloat values[]);

/**
 修改变化的值
 */
@property (readwrite, nonatomic, copy) void (^writeBlock)(id obj, const CGFloat values[]);

/**
 决定动画变化的间隔的阈(yu第四声)值;值越大,writeBlock的调用次数越少;
 */
@property (readwrite, nonatomic, assign) CGFloat threshold;

@end


/**
 常见的 CALayer 属性名称
 */
extern NSString * const kPOPLayerBackgroundColor;
extern NSString * const kPOPLayerBounds;
extern NSString * const kPOPLayerCornerRadius;
extern NSString * const kPOPLayerBorderWidth;
extern NSString * const kPOPLayerBorderColor;
extern NSString * const kPOPLayerOpacity;
extern NSString * const kPOPLayerPosition;
extern NSString * const kPOPLayerPositionX;
extern NSString * const kPOPLayerPositionY;
extern NSString * const kPOPLayerRotation;
extern NSString * const kPOPLayerRotationX;
extern NSString * const kPOPLayerRotationY;
extern NSString * const kPOPLayerScaleX;
extern NSString * const kPOPLayerScaleXY;
extern NSString * const kPOPLayerScaleY;
extern NSString * const kPOPLayerSize;
extern NSString * const kPOPLayerSubscaleXY;
extern NSString * const kPOPLayerSubtranslationX;
extern NSString * const kPOPLayerSubtranslationXY;
extern NSString * const kPOPLayerSubtranslationY;
extern NSString * const kPOPLayerSubtranslationZ;
extern NSString * const kPOPLayerTranslationX;
extern NSString * const kPOPLayerTranslationXY;
extern NSString * const kPOPLayerTranslationY;
extern NSString * const kPOPLayerTranslationZ;
extern NSString * const kPOPLayerZPosition;
extern NSString * const kPOPLayerShadowColor;
extern NSString * const kPOPLayerShadowOffset;
extern NSString * const kPOPLayerShadowOpacity;
extern NSString * const kPOPLayerShadowRadius;

/**
 常见的 CAShapeLayer 属性名称
 */
extern NSString * const kPOPShapeLayerStrokeStart;
extern NSString * const kPOPShapeLayerStrokeEnd;
extern NSString * const kPOPShapeLayerStrokeColor;
extern NSString * const kPOPShapeLayerFillColor;

/**
 常见的 NSLayoutConstraint 属性名称
 */
extern NSString * const kPOPLayoutConstraintConstant;


#if TARGET_OS_IPHONE

/**
 常见的 UIView 属性名称
 */
extern NSString * const kPOPViewAlpha;
extern NSString * const kPOPViewBackgroundColor;
extern NSString * const kPOPViewBounds;
extern NSString * const kPOPViewCenter;
extern NSString * const kPOPViewFrame;
extern NSString * const kPOPViewScaleX;
extern NSString * const kPOPViewScaleXY;
extern NSString * const kPOPViewScaleY;
extern NSString * const kPOPViewSize;
extern NSString * const kPOPViewTintColor;

/**
 常见的 UIScrollView 属性名称
 */
extern NSString * const kPOPScrollViewContentOffset;
extern NSString * const kPOPScrollViewContentSize;
extern NSString * const kPOPScrollViewZoomScale;
extern NSString * const kPOPScrollViewContentInset;

/**
 常见的 UITableView 属性名称
 */
extern NSString * const kPOPTableViewContentOffset;
extern NSString * const kPOPTableViewContentSize;

/**
 常见的 UICollectionView 属性名称
 */
extern NSString * const kPOPCollectionViewContentOffset;
extern NSString * const kPOPCollectionViewContentSize;

/**
 常见的 UINavigationBar 属性名称
 */
extern NSString * const kPOPNavigationBarBarTintColor;

/**
 常见的 UIToolbar 属性名称
 */
extern NSString * const kPOPToolbarBarTintColor;

/**
 常见的 UITabBar 属性名称
 */
extern NSString * const kPOPTabBarBarTintColor;

/**
 常见的 UILabel 属性名称
 */
extern NSString * const kPOPLabelTextColor;

自定义属性动画 POPPropertyAnimation

#import 
#import 

/**
 @abstract Flags for clamping animation values.
 @discussion Animation values can optionally be clamped to avoid overshoot. kPOPAnimationClampStart ensures values are more than fromValue and kPOPAnimationClampEnd ensures values are less than toValue.
 */
typedef NS_OPTIONS(NSUInteger, POPAnimationClampFlags)
{
  kPOPAnimationClampNone        = 0,
  kPOPAnimationClampStart       = 1UL << 0,
  kPOPAnimationClampEnd         = 1UL << 1,
  kPOPAnimationClampBoth = kPOPAnimationClampStart | kPOPAnimationClampEnd,
};

/**
 semi-concrete 属性动画子类
 */
@interface POPPropertyAnimation : POPAnimation

/**
 做动画的属性
 */
@property (strong, nonatomic) POPAnimatableProperty *property;

/**
 做动画的初始值;
 如果未设置,则在动画启动的时候,按照对象当前的值作为初始值;
 */
@property (copy, nonatomic) id fromValue;

/**
 做动画的值
 如果未设置,则在动画启动的时候,按照对象当前的值作为初始值;
 */
@property (copy, nonatomic) id toValue;

/**
 四舍五入系数;
 作用让动画变得更圆滑; 默认0; 取1.0和取整值之间;
 */
@property (assign, nonatomic) CGFloat roundingFactor;

/**
 就是让动画保证在fromValue和tovalue之间,不会越界;
 */
@property (assign, nonatomic) NSUInteger clampMode;

/**
 动画的值是叠加,而不是设置;
 默认 NO;
 */
@property (assign, nonatomic, getter = isAdditive) BOOL additive;

@end

值得关注的 POP 周边

POP-HandApp 包含了大量动画的操作方法和上述介绍的实例。

AGGeometryKit-POP 通过 POP 对图片进行变形操作,非常酷。

POP-MCAnimate POP 的一个封装,可以让你更方便的使用 POP。

Rebound POP 的 Android 部分实现,主要是 Spring 的效果,移植自 Facebook 的rebound-js。

5 Steps For Using Facebook Pop

五步使用 Facebook Pop

五步使用 Facebook Pop

Facebook Pop 动画框架详细解析

Facebook Pop 源码注释

从 CoreAnimation 到 POP

缓动函数速查表

iOS 核心动画高级技巧

最后

POP 是一个新的里程碑,通过 POP,动画的开发门槛大大降低,并且实现了丰富的属性操作,其倡导的可中断式动画交互会革命性也值得我们仔细研究体会,想必不久就会涌现大量富有活力的 App ,感谢 Facebook,感谢开源。 Long live Opensource


内容来自于 丶纳凉 、里脊串 、Cocoachina 的博客

你可能感兴趣的:(iOS 开发之使用 Facebook POP)