使用 Facebook 开源动画库 POP 实现真实衰减与弹簧动画

  • POP动画引擎简介

  • POP动画引擎中Layer与CALayer的联系与区别

  • 用POP引擎实现衰减动画

  • 用POP引擎实现弹簧动画

  • POP动画引擎简介

  1. POP动画是Facebook公司开源的
  2. POP动画主要实现了真实物理系的动画效果(衰减动画和弹簧动画)
  3. POP动画引擎的动画效果非常流畅,因为它使用了CADisplayLink来刷新画面(帧),一秒钟刷新帧数为60帧,接近于游戏开发引擎
  4. POP动画引擎自成体系,与系统的CoreAnimation有很大的区别,但使用上非常相似
  • 下面我们来测试一下CADisplayLink一秒钟刷新的帧数
  1. 创建CADisplayLink实例并绑定方法
  2. 在方法中实现打印
  3. 加入NSRunloop,1秒时开始,2秒时结果
#import "ViewController.h"

@interface ViewController ()

@property (nonatomic, strong) CADisplayLink *displayLink;
@property (nonatomic) NSInteger count;

@end

@implementation ViewController

- (void)viewDidLoad {
    [super viewDidLoad];
    
    self.displayLink = [CADisplayLink displayLinkWithTarget:self selector:@selector(displayLinkEvent:)];

    [self performSelector:@selector(eventOne) withObject:nil afterDelay:1.f];
    [self performSelector:@selector(eventTwo) withObject:nil afterDelay:2.f];
}

#pragma mark displayLink Event
- (void)displayLinkEvent:(id)object
{
    self.count ++;
    NSLog(@"count = %ld", self.count);
}

- (void)eventOne
{
    // 加入RunLoop
    [self.displayLink addToRunLoop:[NSRunLoop currentRunLoop] forMode:NSDefaultRunLoopMode];
}

- (void)eventTwo
{
    // 销毁
    [self.displayLink invalidate];
}

@end```

- 打印结果:

![Paste_Image.png](http://upload-images.jianshu.io/upload_images/189984-db2e6d0d1136e804.png?imageMogr2/auto-orient/strip|imageView2/2/w/1240)

我们发现打印了60次。

######POP 动画引擎中 Layer 与 CALayer 的联系与区别

1. 使用POP动画与使用CALyer动画非常相似
2. POP动画的执行没有中间状态
3. POP动画是对CALayer动画的扩充,但不能实现所有的CALayer的动画效果
4. POP动画可以应用到各种对象上,并不仅仅是CALayer上

- 区别

![Paste_Image.png](http://upload-images.jianshu.io/upload_images/189984-ec4dfdf7c82454e6.png?imageMogr2/auto-orient/strip|imageView2/2/w/1240)

```objc
#import "ViewController.h"

@interface ViewController ()

@property (nonatomic, strong) CALayer *normalLayer; // 普通Layer
@property (nonatomic, strong) CALayer *popLayer;    // POPLayer

@end

@implementation ViewController

- (void)viewDidLoad {
    [super viewDidLoad];

    // 处理normalLayer
//    [self accessNormalLayer];
    
    // 处理PopLayer
    [self accessPopLayer];
}

#pragma amrk - 处理popLayer
- (void)accessPopLayer
{
    // 初始化layer
    self.popLayer = [CALayer layer];
    self.popLayer.frame = CGRectMake(100, 100, 100, 100);
    self.popLayer.backgroundColor = [UIColor redColor].CGColor;
    [self.view.layer addSublayer:self.popLayer];
    
    // 初始化pop的基础动画
    POPBasicAnimation *popAnimation = [POPBasicAnimation animationWithPropertyNamed:kPOPLayerPosition];
    popAnimation.toValue = [NSValue valueWithCGPoint:CGPointMake(100 + 50, 400)];
    popAnimation.duration = 4.f;
    popAnimation.timingFunction = [CAMediaTimingFunction functionWithName:kCAMediaTimingFunctionLinear];
    [self.popLayer pop_addAnimation:popAnimation forKey:nil];
    
    // 1.5秒移除动画
    [self performSelector:@selector(removePopAnimation) withObject:nil afterDelay:1.5f];
}

#pragma mark - 处理normalLayer
- (void)accessNormalLayer
{
    // 初始化layer
    self.normalLayer = [CALayer layer];
    self.normalLayer.frame = CGRectMake(100, 100, 100, 100);
    self.normalLayer.backgroundColor = [UIColor redColor].CGColor;
    [self.view.layer addSublayer:self.normalLayer];
    
    // 初始化动画
    CABasicAnimation *basicAnimation = [CABasicAnimation animationWithKeyPath:@"position"];
    basicAnimation.fromValue = [NSValue valueWithCGPoint:self.normalLayer.position];
    basicAnimation.toValue = [NSValue valueWithCGPoint:CGPointMake(100 + 50, 400)];
    basicAnimation.duration = 4.f;
    basicAnimation.timingFunction = [CAMediaTimingFunction functionWithName:kCAMediaTimingFunctionEaseIn];
    
    // 设置结束位置的值
    self.normalLayer.position = CGPointMake(100 + 50, 400);
    [self.normalLayer addAnimation:basicAnimation forKey:nil];
    
    // 1.5秒后移除动画
    [self performSelector:@selector(removeNormalAnimation) withObject:nil afterDelay:1.5f];
    
}

#pragma mark - 移除pop动画
- (void)removePopAnimation
{
    [self.popLayer pop_removeAllAnimations];
}

#pragma mark - 移除普通动画效果
- (void)removeNormalAnimation
{
    CALayer *layer = self.normalLayer.presentationLayer;
    // 移动时候的值
    NSLog(@"%@", NSStringFromCGRect(self.normalLayer.frame));
    // layer结束的值
    NSLog(@"%@", NSStringFromCGRect(layer.frame));
    [self.normalLayer removeAllAnimations];
}

@end```

- 效果图
- 使用CoreBasicAnimation的运行结果
![Paste_Image.png](http://upload-images.jianshu.io/upload_images/189984-09a41f2324b22a31.png?imageMogr2/auto-orient/strip|imageView2/2/w/1240)

- 使用POPAnimation的运行结果
![Paste_Image.png](http://upload-images.jianshu.io/upload_images/189984-7589025965e86126.png?imageMogr2/auto-orient/strip|imageView2/2/w/1240)

- 我们通过对CoreAnimation和POPAnimation动画的对比,发现同样是1.5秒之后结束动画,但是CoreBasicAnimation动画在1.5秒的之后直接跳到规定的位置,但是POP动画却停留在了1.5处,这就是两者最大的区别,我们在设计动画的时候,希望动画是连贯的,这就是我们最不能容忍的问题,而POP动画帮我们解决了这个问题。

- 用 POP 动画引擎实现衰减动画
1. 衰减动画由POPDecayAnimation来实现
2. 需要精确计算停止运动瞬间的加速度才能够用衰减动画做出真实的效果

```objc
#import "ViewController.h"

@interface ViewController ()

@property(nonatomic, strong) UIButton * button;

@end

@implementation ViewController

- (void)viewDidLoad {
    [super viewDidLoad];

    self.button = [[UIButton alloc] initWithFrame:CGRectMake(100, 100, 100, 100)];
    self.button.backgroundColor = [UIColor redColor];
    self.button.layer.cornerRadius = 50;
    self.button.layer.masksToBounds = YES;
    self.button.center = self.view.center;
    [self.view addSubview:self.button];
    [self.button addTarget:self action:@selector(buttonAction:) forControlEvents:UIControlEventTouchUpInside];
    
    // 初始化拖拽手势
    UIPanGestureRecognizer *panGesture = [[UIPanGestureRecognizer alloc] initWithTarget:self action:@selector(handlePanGesture:)];
    [self.button addGestureRecognizer:panGesture];
}

- (void)handlePanGesture:(UIPanGestureRecognizer *)recognizer
{
    // 获取拖拽点
    CGPoint translation = [recognizer translationInView:self.view];
    // 实现拖拽移动
    recognizer.view.center = CGPointMake(recognizer.view.center.x + translation.x, recognizer.view.center.y + translation.y);
    // 恢复坐标系
    [recognizer setTranslation:CGPointMake(0, 0) inView:self.view];
    // 判断手势停止时,执行代码
    if (recognizer.state == UIGestureRecognizerStateEnded) {
        NSLog(@"手势停止了");
        // 获取加速度值
        CGPoint velocity = [recognizer velocityInView:self.view];
        // 初始化POPDecay衰减动画
        POPDecayAnimation *decayAnimation = [POPDecayAnimation animationWithPropertyNamed:kPOPLayerPosition];
        decayAnimation.velocity = [NSValue valueWithCGPoint:velocity];
        // 添加
        [recognizer.view.layer pop_addAnimation:decayAnimation forKey:nil];
        
    }
    
}
- (void)buttonAction:(UIButton *)button
{
    [button pop_removeAllAnimations];
}

@end```

- 衰减动画运行效果
![decayAnimation.gif](http://upload-images.jianshu.io/upload_images/189984-1aa7590a617da166.gif?imageView2/2/w/1240)

- 用 POP 动画引擎实现弹簧动画
1. 弹簧动画是由POPStringAnimation来实现的
2. 弹簧的质量速度时间的值都可以设置

```objc
#import "ViewController.h"

@interface ViewController ()

@property (nonatomic, strong) UIView *showView;
@end

@implementation ViewController

- (void)viewDidLoad {
    [super viewDidLoad];
    
    self.view.backgroundColor = [UIColor blackColor];

    self.showView = [[UIView alloc] init];
    self.showView.frame = CGRectMake(0, 0, 50, 50);
    self.showView.backgroundColor = [UIColor cyanColor];
    self.showView.center = self.view.center;
    [self.view addSubview:self.showView];
    
    
    [self performSelector:@selector(startSpringAnimation) withObject:nil afterDelay:1.f];
}

- (void)startSpringAnimation
{
    // 初始化SpringAnimation动画
    POPSpringAnimation *sizeAnimation = [POPSpringAnimation animationWithPropertyNamed:kPOPLayerBounds];
    sizeAnimation.springSpeed = 0.f;    // 动画的速度
    sizeAnimation.toValue = [NSValue valueWithCGRect:CGRectMake(0, 0, 100, 100)];
    
    // 添加动画
    [self.showView pop_addAnimation:sizeAnimation forKey:nil];

}

@end```

- 弹簧动画运行效果
![pop.gif](http://upload-images.jianshu.io/upload_images/189984-a5bc9c72a447901a.gif?imageView2/2/w/1240)

你可能感兴趣的:(使用 Facebook 开源动画库 POP 实现真实衰减与弹簧动画)