美文网首页
iOS 动画

iOS 动画

作者: owenqi | 来源:发表于2017-02-10 11:45 被阅读0次

    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; // 存在过渡动画
    
    • UIView 的 alpha 和 CALayer 的 opacity 应该是一致的, opacity 会和 alpha 同步变化

    Layer 树

    • Render Tree (Private): 渲染 // 不讨论
    • Model Layer Tree: [layer modelLayer]: Layer 属性的最终值
    • Presentation Layer Tree: [layer presentationLayer]: 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

    • 对 layer 属性的改变, 在渲染动画之前, 系统会产生一个 Transaction 事务, 打包所有动画操作, 然后提交给 RenderTree
    • 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];
    
    • 显式事务: 放在 begin 和 commit 中间的操作, 显式声明了事务的过程
    • 隐式事务: 不声明事务过程, 但系统在操作过程中会自动添加事务, 比如说默认的动画时长 0.25s

    显式动画

    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

    • 动画过程中, 改变的是 PresentationLayer 的值, 但 modelLayer 的值不会被改变. 动画结束的时候, 动画被移除, 所以位置会恢复到 modelLayer 的值, 即还原到原来的位置
    • addAnimation:forKey:这个过程中, add 进去的 animation 会被拷贝的, 所以添加完动画之后, 对动画进行修改将不生效

    CAMediaTiming

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

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

    • 在动画结束的时候, 再进行一次位置的设置操作, 将 modelLayer 的值设置成位移之后应该变成的值
      • 在改变 layer 的位置的时候, 会产生隐式动画, 导致两个动画的产生
      • 保持两个动画的 key 值一致, 后面添加的动画会覆盖前面添加的动画
      • 所以, 可以通过设置一致的动画 key 值, 将显式动画覆盖隐式动画
    // ------ animation ------
    layer.position.x, byValue = 100;
    // ------ animation stop ------
    layer.position.x += 100;
    

    CoreAnimation Timing

    • CAMediaTiming System
    • CACurrentMediaTime()获取创建的 CALayer 在系统时间轴上对应的时间是什么
    - (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
    

    暂停动画

    • 动画过程中, 调用暂停方法, 动画暂停在调用时的状态
    • timeOffset = 0 && speed = suitableValue;
    • 不能通过修改 animation.speed = 0 && animation.timeOffset = xxx; 进行设置
      • 因为 animation 添加到 Layer 上之后, 就被复制了, 之后再修改 animation 的属性不会对动画产生影响
      • 所以要使用 layer.speed = 0 && layer.timeOffset = xxx;
    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
    
    • 动画过程
      1. time: tp0; speed=1, begin=0, lt=lt0, offset=0, => tp0 = lt0; // 动画开始
      2. time: tp1; speed=0, begin=0, lt=lt1, offset=0, => tp1 = lt1; // 动画暂停
        • 将 speed 置为0, => lt1' = offset = 0
        • 因为 lt1' 为0, 所以动画会恢复到原来的位置. 要保存动画位置状态, 需要让 lt1' = lt1, 即 offset = lt1
      3. time: tp2; speed=1, begin=0, lt=lt2, offset=0, => tp2 = lt2;

    CAMediaTiming 动画结束后保持

    • fillMode属性
    @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; // 动画速率
    
    • calculationMode 对动画运动路径产生变化
    // 下面三个参数用于定制 calculateMode 路径
    @property (nullable, copy) NSArray<NSNumber *> *tensionValues;
    @property (nullable, copy) NSArray<NSNumber *> *continuityValues;
    @property (nullable, copy) NSArray<NSNumber *> *biasValues;
    
    • additive 对动画参数进行叠加, 即在原来动画的基础上增量增加动画
    • path 可以覆盖 values 属性
      • path 在被赋值的时候是通过复制完成的
      • 需要将 calculateMode 设置为 kCAAnimationPaced
    • rotationMode 在进行动画的时候自动进行旋转

    CATransition

    • type 描述动画的形式
      • cube
      • oglFlip
      • ... // CoreAnimation 2-3 06:00
    • subtype 动画的方向
    • startProgress 整个动画起始时间
    • endProgress
    • filter 定制动画的效果

    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

    • animations 动画组合
    • timingFunction 会叠加
    • 如果要设置 animation1 的 beginTime, 可以直接设置, 因为 animation1 属于 group 动画, group 是从0开始的
    • 动画的 duration 超出了 group 的 duration, 则会被截断
    • speed 和 timingFunction 都是会叠加的
    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

    • shapeLayer
    • fillColor
    • path 这个 path 不支持隐式动画
    • stroke; line
    // 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];
    }
    
    • fillColor 填充颜色
    • fillRule 填充规则
    • lineWidth 线的宽度
    • lineCap 线头形状
    • lineJoin 连接时
    • miterLimit
    • lineDashPhase 虚线从哪里开始画
    • lineDashPattern 虚线的样式 @[@10, @10]: 实线部分10, 虚线部分10

    利用 CAShapeLayer 实线一个进度视图

    CAReplicatorLayer

    • instanceCount
    • instanceDelay
    • instanceTransform
    • instanceColor
      • instanceRedOffset
      • instanceGreenOffset
      • instanceBlueOffset
      • instanceAlphaOffset
    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

    • CAEmitterLayer 粒子发射器
    • Emitter
    • CAEmitterCell

    相关文章

      网友评论

          本文标题:iOS 动画

          本文链接:https://www.haomeiwen.com/subject/npsgittx.html