Cowboy Tech

缓动函数与关键帧动画

缓动函数简介

  1. 缓动函数的动画效果是建立在CALayer层级的关键帧动画基础之上
  2. 缓动函数是一系列模拟物理效果(如抛物线)方程式的统称,用以计算给定两点之间的插值
  3. 两点之间插的值越多,效果越好,但是会耗费更多的性能
  4. 只有理解了缓动函数的原理才有可能写出自己想要的效果

缓动函数与关键帧动画的联系

  1. 关键帧动画需要提供很多的帧来完善动画效果
  2. 关键帧动画的帧可以通过一定的数学计算来提供需要的帧数
  3. 关键帧动画只需要提供起始点,结束点,就可以通过缓动函数来计算中间“缺失”的帧
  4. 缓动函数可以指定计算出多少帧
  5. 帧数越多,动画越流畅,但同时耗费更多的GPU性能。1秒钟60帧最多了,写得再大也是浪费GPU

代码

- (void)viewDidLoad {
[super viewDidLoad];
// Do any additional setup after loading the view, typically from a nib.

// 添加显示用的view
UIView *showView = [[UIView alloc] initWithFrame:CGRectMake(0, 0, 100, 100)];
showView.layer.cornerRadius  = 50;
showView.layer.masksToBounds = YES;
showView.backgroundColor     = [UIColor redColor];
[self.view addSubview:showView];

//    // 基本动画类型
//    CABasicAnimation *basicAnimation = [CABasicAnimation animation];
//    basicAnimation.keyPath           = @"position";
//    basicAnimation.duration          = 4.f;
//
//    // fromValue = A | toValue = B
//    basicAnimation.fromValue = [NSValue valueWithCGPoint:showView.center];
//    basicAnimation.toValue   = [NSValue valueWithCGPoint:CGPointMake(200, 200)];
//
//    showView.center = CGPointMake(200, 200);
//    [showView.layer addAnimation:basicAnimation forKey:nil];

// 关键帧动画类型
CAKeyframeAnimation *keyFrameAnimation = [CAKeyframeAnimation animation];
keyFrameAnimation.keyPath              = @"position";
keyFrameAnimation.duration             = 4.f;
keyFrameAnimation.values = [YXEasing calculateFrameFromPoint:showView.center
                                                     toPoint:CGPointMake(200, 200)
                                                        func:BounceEaseInOut
                                                  frameCount:30 * 4];
showView.center = CGPointMake(200, 200);
[showView.layer addAnimation:keyFrameAnimation forKey:nil];
}

用缓动函数模拟弹簧效果

  1. 使用easeOutElastic函数来创建弹簧效果
  2. 将easeOutElastic创建出来的帧数组添加到关键帧动画中
  3. 弹簧效果用途

代码

#import "ViewController.h"
#import "YXEasing.h"
@interface ViewController ()
@property (nonatomic, strong) CALayer *secLayer; // 秒针layer
@property (nonatomic, strong) NSTimer *timer;    // 定时器
@end

@implementation ViewController

- (void)viewDidLoad {
[super viewDidLoad];
// Do any additional setup after loading the view, typically from a nib.

// 创建一个表盘
UIView *showView            = [[UIView alloc] initWithFrame:CGRectMake(0, 0, 300, 300)];
showView.center             = self.view.center;
showView.layer.borderWidth  = 1.f;
showView.layer.cornerRadius = 150;
showView.layer.borderColor  = [UIColor redColor].CGColor;
[self.view addSubview:showView];

// 创建出秒针layer
self.secLayer                 = [CALayer layer];
self.secLayer.anchorPoint     = CGPointMake(0, 0);
self.secLayer.frame           = CGRectMake(150, 150, 1, 150);
self.secLayer.backgroundColor = [UIColor blackColor].CGColor;
[showView.layer addSublayer:self.secLayer];

// 创建定时器
self.timer = [NSTimer scheduledTimerWithTimeInterval:1.f
                                              target:self
                                            selector:@selector(timerEvent)
                                            userInfo:nil
                                             repeats:YES];
}

- (void)timerEvent {

static int i = 1;

CGFloat oldValue = DEGREES_TO_RADIANS((360 / 60.f) * i++);
CGFloat newValue = DEGREES_TO_RADIANS((360 / 60.f) * i);

// 创建关键帧动画
CAKeyframeAnimation *keyFrameAnimation = [CAKeyframeAnimation animation];
keyFrameAnimation.keyPath              = @"transform.rotation.z";
keyFrameAnimation.duration             = 0.5;
keyFrameAnimation.values               = [YXEasing calculateFrameFromValue:oldValue
                                                                   toValue:newValue
                                                                      func:ElasticEaseOut
                                                                frameCount:0.5 * 30];

self.secLayer.transform = CATransform3DMakeRotation(newValue, 0, 0, 1);
[self.secLayer addAnimation:keyFrameAnimation forKey:nil];
}

用缓动函数模拟碰撞效果

  1. 使用easeOutBounce函数来创建弹簧效果
  2. 将easeOutBounce创建出来的帧数组添加到关键帧动画中
  3. 碰撞效果用途

代码

- (void)viewDidLoad {
[super viewDidLoad];
// Do any additional setup after loading the view, typically from a nib.
// 创建图片view
UIImageView *imageView = [[UIImageView alloc] initWithFrame:CGRectMake(0, 0, 320, 320)];
imageView.image        = [UIImage imageNamed:@"pic"];
imageView.contentMode  = UIViewContentModeScaleAspectFill;
[self.view addSubview:imageView];

// 创建关键帧动画(移动距离的动画)
CAKeyframeAnimation *keyFrameAnimation = [CAKeyframeAnimation animation];
keyFrameAnimation.keyPath              = @"position";
keyFrameAnimation.duration             = 2.f;
keyFrameAnimation.values               = \
[YXEasing calculateFrameFromPoint:imageView.center
                          toPoint:CGPointMake(320 / 2.f, 320 / 2.f + 240)
                             func:BounceEaseOut
                       frameCount:2 * 30];

// 添加动画
imageView.center = CGPointMake(320 / 2.f, 320 / 2.f + 240);
[imageView.layer addAnimation:keyFrameAnimation forKey:nil];
}

用缓动函数模拟衰减效果

  1. 使用easeOutCubic函数来创建弹簧效果
  2. 将easeOutCubic创建出来的帧数组添加到关键帧动画中
  3. 衰减效果用途

代码

- (void)viewDidLoad {
[super viewDidLoad];
// Do any additional setup after loading the view, typically from a nib.
// 背景变暗的view
UIView *backView         = [[UIView alloc] initWithFrame:self.view.bounds];
backView.backgroundColor = [UIColor blackColor];
backView.alpha           = 0;
[UIView animateWithDuration:1.f animations:^{
    backView.alpha       = 0.3;
}];
[self.view addSubview:backView];

// 创建模拟的菜单
UIImageView *imageView = [[UIImageView alloc] initWithFrame:CGRectMake(320, 0, 320, 568)];
imageView.image        = [UIImage imageNamed:@"pic"];
[self.view addSubview:imageView];

// 创建关键帧动画
CAKeyframeAnimation *keyFrameAnimation = [CAKeyframeAnimation animation];
keyFrameAnimation.keyPath              = @"position";
keyFrameAnimation.duration             = 1.f;
keyFrameAnimation.values               = \
[YXEasing calculateFrameFromPoint:imageView.center
                          toPoint:CGPointMake(self.view.center.x + 100, self.view.center.y)
                             func:CubicEaseOut
                       frameCount:1 * 30];

// 加载关键帧动画
imageView.center = CGPointMake(self.view.center.x + 100, self.view.center.y);
[imageView.layer addAnimation:keyFrameAnimation forKey:nil];
}