iOS 动画

2017-02-10  本文已影响0人  owenqi

CAAnimation - 1: CABasicAnimation

隐式动画

_myView = [[UIView alloc] initWithFrame:CGRectMake(50, 100, 50, 50)];
_myView.backgroundColor = [UIColor orangeColor];
[self.view addSubview:_myView];
    
_myLayer = [CALayer layer];
_myLayer.frame = CGRectMake(50, 200, 50, 50);
_myLayer.backgroundColor = [UIColor purpleColor].CGColor;
[self.view.layer addSublayer:_myLayer];

_myView.alpha = 0.2; // 不存在过渡动画
_myLayer.opacity = 0.2; // 存在过渡动画

Layer 树

layer 运动过程, layer.position.x 初始为0
假若 layer 向右平移 100pt, layer.position.x, 所以变化如下
((CALayer *)[layer modelLayer]).position.x = 100 // 直接到达100
((CALayer *)[layer presentationLayer]).position.x = 0 ... 100 // 在运动过程中缓慢增加到100

CATransaction

+ (void)begin;
// 所有操作应该放在这两个方法之中
+ (void)commit;

+ (void)setAnimationDuration:(CFTimeInterval)dur; // 设置默认的时间值

+ (void)disableActions; // 禁止隐式动画

+ (void)setCompletionBlock:(nullable void(^)(void))block; // 完成动画之后的回调
[CATransaction begin];
[CATransaction setCompletionBlock:^{
    NSLog(@"Set position animation completed.");
}];
[CATransaction setDisableActions:YES]; // 禁用隐式动画
[CATransaction setAnimationDuration:1]; // 动画时间
[CATransaction setAnimationTimingFunction:[CAMediaTimingFunction functionWithName:kCAMediaTimingFunctionEaseInEaseOut]];
_myLayer.position = CGPointMake(_myLayer.position.x + 100, _myLayer.position.y);
[CATransaction commit];

显式动画

CABasicAnimation 属性

哪个属性动
/* CABasicAnimation class */
// 指定哪个属性动, keyPath 是一个字符串
// "opacity" 透明度; "position" 位置; "position.x" 位置的 x 值; ......
// [CABasicAnimation animationWithKeyPath:@"position.x"]; 创建一个 BasicAnimation 对象

+ (instancetype)animationWithKeyPath:(nullable NSString *)path;
@property (nullable, copy) NSString *keyPath;
怎么动
// 怎么动
// 这三个值必须是一致的类型
// 三个属性可以为空, 但是不能设置超过两个属性值
@property (nullable, strong) id fromValue;
@property (nullable, strong) id toValue;
@property (nullable, strong) id byValue;
// 只设置以下值会发生的情况
fromValue && toValue    // fromValue ~ toValue
fromValue && byValue    // fromValue ~ (fromValue + byValue)
toValue && byValue      // (toValue - byValue) ~ toValue
fromValue                   // fromValue ~ PresentationLayer.value (fromValue ~ currentValue)
toValue                 // PresentationLayer.value ~ toValue (currentValue ~ toValue)
byValue                 // PresentationLayer.value ~ PresentationLayer.value + byValue (currentValue ~ currentValue + byValue)
谁动
// 给 layer 添加动画
- (void)addAnimation:(CAAnimation *)anim forKey:(nullable NSString *)key;
// 获取动画的名字
- (nullable NSArray<NSString *> *)animationKeys;
// 获取指定名字的动画对象
- (nullable CAAnimation *)animationForKey:(NSString *)key;

CABasicAnimation 动画demo

CAMediaTiming

@protocol CAMediaTiming

@property CFTimeInterval duration;  // 虽然默认是0, 但是还是会保持动画隐式动画默认的0.25s
@property float repeatCount;        // 动画的重复次数, 设置 HUGE_VALF 可以设置成无限次
@property CFTimeInterval repeatDuration; // 每一次动画持续的时间, 和 repeatCount 不能同时设置, 系统通过计算总时间和每次动画时长计算重复的次数
@property BOOL autoreverses; // 回复

@end

动画结束之后如何不返回原来的位置

// ------ animation ------
layer.position.x, byValue = 100;
// ------ animation stop ------
layer.position.x += 100;

CoreAnimation Timing

- (CFTimeInterval)convertTime:(CFTimeInterval)t fromLayer:(nullable CALayer *)l;
- (CFTimeInterval)convertTime:(CFTimeInterval)t toLayer:(nullable CALayer *)l;
@protocol CAMediaTiming

@property CFTimeInterval beginTime; // 动画开始的时间, 
// animation.beginTime = [layer convertTime:CACurrentMediaTime() fromLayer:nil] + 1; 表示 layer 动画添加之后的一秒执行

@property float speed; // 让整个动画加速,
// 设置动画时长 duration 为10, 默认 speed 为1; 当 speed 设置成2的时候, 动画将在5秒结束, 当 speed 设置成5的时候, 动画将在2秒结束, 以此类推

@property CFTimeInterval timeOffset; // 相当于把动画的运动作为一个循环, 设置 timeOffset 属性, 将会从 offset 的时间点进行动画, 结束之后循环到动画开始的时候进行运动
// 相对于整个动画的时长进行的 offset, 不会被 speed 影响

@end

暂停动画

lt = (tp - begin) * speed + offset
// lt: layer local time
// tp: parent time
// begin: default 0
// speed: default 1
// offset: default 0

// 所以: lt = (tp - 0) * 1 + 0 => lt = tp
// lt = [layer convertTime:CACurrentMedaiTime() fromLayer:nil];
// tp = (lt - offset) / speed + begin

CAMediaTiming 动画结束后保持

@property (copy) NSString *fillMode;
CABasicAnimation *animation = [CABasicAnimation animationWithKeyPath:@"backgroundColor"];
animation.fromValue = (id)[UIColor yellowColor].CGColor; // 从黄色变化到橙色
animation.toValue = (id)[UIColor orangeColor].CGColor;
animation.duration = 2;
animation.fillMode = kCAFillModeForwards; // 动画结束之后保存动画状态, 需要将 layer 动画设置为不移除
animation.beginTime = [layer convertTime:CACurrentMediaTime() fromLayer:nir] + 1; // 延迟一秒开始动画
[layer addAnimation:animation forKey:@"backgroundColor"];

CAMediaTiming - timingFunction

+ (instancetype)functionWithName:(NSString *)name;
+ (instancetype)functionWithControlPoints:(float)c1x :(float)c1y :(float)c2x :(float)c2y;

CAAnimation - 2:

CASpringAnimation

@interface CASpringAnimation : CABasicAnimation
@property CGFloat mass;         // 质量, 弹簧物品的质量越大, 惯性越大
@property CGFloat stiffness;    // 刚度, 相当于拉弹簧的力
@property CGFloat damping;      // 阻尼; 取值范围 >= 0
@property CGFloat initialVelocity; // 初始速度

@property (readonly) CFTimeInterval settlingDuration; // 弹簧预估的时间

CAKeyFrameAnimation

@interface CAKeyFrameAnimation : CAPropertyAnimation
@property (nullable, copy) NSArray *values; // 进行动画的值 包含第一帧和最后一帧
@property (nullable, copy) NSArray<NSNumber *> *keyTimes; // 分别动画的时长 包含第一帧和最后一帧
@property (nullable, copy) NSArray<CAMediaTimingFunction *> *timingFunctions; // 动画速率
// 下面三个参数用于定制 calculateMode 路径
@property (nullable, copy) NSArray<NSNumber *> *tensionValues;
@property (nullable, copy) NSArray<NSNumber *> *continuityValues;
@property (nullable, copy) NSArray<NSNumber *> *biasValues;

CATransition

CALayer 实现翻转的动画

CATransition *animation = [CATransition animation];
animation.duration = 1;
animation.type = @"oglFlip";
animation.subtype = kCATransitionFromLeft;
animation.timingFunction = [CAMediaTimingFunction functionWithName:kCAMediaTimingFunctionEaseInEaseOut];
[parentView.layer addAnimation:animation forKey:@"myTrans"];
// 在动画中, 动画的 key 值都会被设置为 kCATransition
subview1.hidden = !subview1.hidden;
subview2.hidden = !subview2.hidden;

CAAnimationGroup

CAAnimationGroup *group = [CAAnimationGroup animation];
group.duration = 2;
group.animations = @[animation1, animation2];
group.timingFunction = [CAMediaTimingFunction functionWithName:kCAMediaTimingFunctionEaseInEaseOut];
group.fillMode = kCAFillModeForward;
group.removedOnCompletion = NO;
[layer addAnimation:group forKey:@"group"];

特殊的 Layers

CAShapeLayer

// path 动画 圆形和正方形进行转换
UIBezierPath *path1 = [UIBezierPath bezierPathWithOvalInRect:layer.bounds];
UIBezierPath *path2 = [UIBezierPath bezierPathWithRect:layer.bounds];
layer.path = path1.CGPath;

- (void)touchesEnded {
    CGPathRef fromValue = layer.path;
    CGPathRef toValue = CGPathEqualToPath(path1.CGPath, fromValue) ? path2.CGPath : path1.CGPath;
    CABasicAnimation *animation = [CABasicAnimation animationWithKeyPath:@"path"];
    animation.fromValue = (__bridge id _Nullable)(fromValue);
    animation.toValue = (__bridge id _Nullable)(toValue);
    animation.duration = 2;
    layer.path = toValue;
    [layer addAnimation:animation forKey:animation.keyPath];
}

利用 CAShapeLayer 实线一个进度视图

CAReplicatorLayer

CAReplicatorLayer *replicatorLayer = [CAReplicatorLayer layer];
replicatorLayer.frame = CGRectMake(0, 200, self.view.bounds.size.width, 50);
[self.view.layer addSublayer:replicatorLayer];

CALayer *layer = [CALayer layer];
layer.backgroundColor = [UIColor blueColor].CGColor;
layer.frame = CGRectMake(0, 0, 20, 50);
[replicatorLayer addSublayer:layer];

replicatorLayer.instanceCount = 10; // 复制10次
replicatorLayer.instanceTransform = CATransform3DMakeTranslation(20, 0, 0);

CAEmitterLayer

上一篇下一篇

猜你喜欢

热点阅读