CABasicAnimation基础动画

1,005 阅读4分钟

上一篇,我们学习了CAShapeLayer+UIBezierPath如何去画图形,那画了图形之后,如果我们想要对自己画的图形进行一些基础动画操作,那怎么去做呢?CABasicAnimation就能满足你。

1. CABasicAnimation

CABasicAnimation是继承于CAAnimation,说明了CABasicAnimation只是动画的实现方式之一。

那谁能用的上呢,这里直接给出答案CALayer。

- (void)addAnimation:(CAAnimation *)anim forKey:(nullable NSString *)key;

CALayer的方法,可以添加动画。

闲话就不说了,我们先来看下CABasicAnimation有哪些属性?

  • fromValue: 属性变化的初始值,默认为0。

  • toValue:     属性变化的结束值,这个值是和keyPath有关。

  • byValue:     属性变化的值。

  • duration:    动画时长。

  • speed: 速度,如果为2,则时长会减半。

  • repeatCount:重复次数,MAXFLOAT则为重复进行。

  • autoreverses:动画执行完后是否逆动画,一般都设置为NO。

  • fillMode:动画结束后的状态。Removed为原始状态。Forwards为最后状态,要配和removedOnCompletion为NO实现。

  • timingFunction:动画速度的变化,Linear为匀速,EaseInOut为头尾慢,中间快,EaseIn为开始慢,接着加速,EaseOut就是开始加速,最后慢。

  • removedOnCompletion:执行动画后要不要删除动画。设置为NO则切换后台或者控制器之间,动画不会消失。

    typedef NS_ENUM(NSInteger, UIViewAnimationCurve) { UIViewAnimationCurveEaseInOut, // slow at beginning and end UIViewAnimationCurveEaseIn, // slow at beginning UIViewAnimationCurveEaseOut, // slow at end UIViewAnimationCurveLinear, };

代码里面也有简单注释 UIViewAnimationCurve 的用法。

2. keyPath常用的值

+ (instancetype)animationWithKeyPath:(nullable NSString *)path;

我们要创建动画,是为谁创建动画了呢,看初始化,是传一个path的属性,那这个是传什么呢,记住啦,就是传CALayer的相关属性,可以是系统的,也可以是继承CALayer的属性。

下面将列一下常用的属性。

  • transform.rotation.z: 围绕z轴变化,toValue就传2π。

  • transform.scale:缩放的比例,1为不变。

  • backgroundColor:背景颜色的变化

  • bounds:视图的大小变化,position是不变的。

  • position:视图中心点的变化。

  • contents:内容的变化。

  • opacity:透明度的变化。

差不都就这些了,简单就够用了。

3. delegate 引用问题

动画中我们也可以用 delegate 来回调动画完成情况。

我们实现代理 CAAnimationDelegate。

/* The delegate of the animation. This object is retained for the 
 * lifetime of the animation object. Defaults to nil. See below for the 
 * supported delegate methods. 
 */
@property(nullable, strong) id <CAAnimationDelegate> delegate;

在这里,我们可以看到 delegate 是强引用。所以为了避免循环引用问题。我们需要在动画释放之后删除动画。

- (void)animationDidStop:(CAAnimation *)anim finished:(BOOL)flag {
     //对应添加的key即可。    
[_animationView.layer removeAnimationForKey:@"transform.translation.z"];
}

那如果动画结束之前,就直接返回了,这时候也会出现内存泄露问题。所以我们还可以通过中间管理来修饰。

@protocol JJAnimationDelegate <NSObject>
@optional
- (void)animationDidStart:(CAAnimation *)anim;
- (void)animationDidStop:(CAAnimation *)anim;
@end

@interface JJAnimationDelegateManager : NSObject <CAAnimationDelegate>
@property (nonatomic, weak) id<JJAnimationDelegate> delegate;
@end

@implementation AnimationDelegateManager

- (void)animationDidStart:(CAAnimation *)anim {
    if (_delegate && [_delegate respondsToSelector:@selector(animationDidStart:)]) {
        [_delegate animationDidStart:anim];
    }
}

- (void)animationDidStop:(CAAnimation *)anim finished:(BOOL)flag {
    if (_delegate && [_delegate respondsToSelector:@selector(animationDidStop:)]) {
        [_delegate animationDidStop:anim];
    }
}
@end

调用方式直接用 

    JJAnimationDelegateManger *manager = [JJAnimationDelegateManger new];    manager.delegate = self;    animation.delegate = manager.delegate;

4. 例子

又到了上代码的时候了,这次我们就做一个圈圈,圈圈旋转的动画,圈圈的4分之三是一种颜色,4分之一是一种颜色。

大概和这个图差不多吧,然后旋转起来。

那我们就利用上一次学的CAShapeLayer+UIBezierPath,把这个图先画出来先。

CGRect rect = CGRectMake(0, 0, 50, 50);
    UIBezierPath *path = [UIBezierPath bezierPathWithOvalInRect:rect];
    // 设置路径画布
    CAShapeLayer *lineLayer = [CAShapeLayer layer];
    lineLayer.lineWidth = 4.0;
    lineLayer.strokeColor = [UIColor yellowColor].CGColor;
    lineLayer.strokeStart = 0;
    lineLayer.strokeEnd = 0.75;
    lineLayer.path = path.CGPath;
    lineLayer.fillColor = [UIColor clearColor].CGColor;
    [view.layer addSublayer:lineLayer];
    
    CAShapeLayer *lineLayer2 = [CAShapeLayer layer];
    lineLayer2.lineWidth = 4.0;
    lineLayer2.strokeColor = [UIColor systemPinkColor].CGColor;
    lineLayer2.strokeStart = 0.75;
    lineLayer2.strokeEnd = 1;
    lineLayer2.path = path.CGPath;
    lineLayer2.fillColor = [UIColor clearColor].CGColor;
    [view.layer addSublayer:lineLayer2];

然后我们再添加上动画效果,这里就用transform.rotation.z为keypath做好。

CABasicAnimation *basicAni = [CABasicAnimation animationWithKeyPath:@"transform.rotation.z"];
    basicAni.toValue = @(M_PI*2);
    basicAni.duration = 1.0;
    basicAni.repeatCount = MAXFLOAT;
    basicAni.autoreverses = false;
    basicAni.fillMode = kCAFillModeForwards;

    [view.layer addAnimation:basicAni forKey:nil];

大功告成,我们看一下效果图。

还不错,那为什么有蓝色呢,去掉背景蓝色就是和上面的图片一样样的了。

到了这里,可能大家又会想,这里只能一种动画,能不能多种进行呢。答案是可以的。

我们用CAAnimationGroup就组合就行。

CABasicAnimation *basicAni = [CABasicAnimation animationWithKeyPath:@"transform.rotation.z"];
    basicAni.toValue = @(M_PI*2);
    basicAni.duration = 1.0;
    basicAni.repeatCount = MAXFLOAT;
    basicAni.autoreverses = false;
    basicAni.fillMode = kCAFillModeForwards;
    
    CABasicAnimation *basicAni2 = [CABasicAnimation animationWithKeyPath:@"position.y"];
    basicAni2.toValue = @(view.center.y + 100);
    basicAni2.duration = 2.0;
    basicAni2.repeatCount = MAXFLOAT;
    basicAni2.fillMode = kCAFillModeForwards;

    CAAnimationGroup *animationGroup = [CAAnimationGroup animation];
    animationGroup.animations = @[basicAni, basicAni2];
    animationGroup.repeatCount = MAXFLOAT;
    animationGroup.duration = 2;

    [view.layer addAnimation:animationGroup forKey:nil];

看效果

气泡浮动动画

注意:要用 centerY 来进行上下浮动,点击的时候位置才不会偏离。

[self.layer removeAllAnimations]; 
CABasicAnimation *animation = [CABasicAnimation animationWithKeyPath:@"position.y"];
animation.fromValue = @(self.centerY);    
animation.toValue = @(self.centerY + arc4random()%5 + 3);
animation.duration = arc4random()%3 + 1;    
animation.repeatCount = CGFLOAT_MAX;    
animation.autoreverses = YES;    
animation.timingFunction = [CAMediaTimingFunction functionWithName:kCAMediaTimingFunctionEaseInEaseOut];    
[self.layer addAnimation:animation forKey:@"basic"];

5. 总结

CABasicAnimation到这里也就差不多了,总结起来有句话就是指定可动画属性后,动画会按照预定的参数持续一定时间由初始值变换为终点值。

很多同学可能心里会想着,这里都只有toValue,fromValue,那如果想要搞多种效果呢,有的,不过是关键帧动画CAKeyframeAnimation,这个下期我们再详细的看看,到时候再看看这些动画又有什么不同呢。