旋转动画

这里写图片描述
再复杂的动画都可以拆分成许多简单的动画组合起来
此动画可以拆分为4个大阶段,对应着4个点之间的动画过程:

每个大阶段又可以拆分为2个小阶段(以第一个和第二个点为例):
1)A点到B点之间的动画:B点不出现,以A点为起点,从A点一直“伸”到B点
2)B点到A点之间的动画:B点出现,以B点为终点,从A点一直“缩”到B点

一、动画实现方式
由于这里仅需要画圆形,我们选择CAShapeLayer来实现。

CAShapeLayer的简介:
是代表一个形状(Shape)的Layer,它是CALayer的子类。
CAShapeLayer初始化需要指定Frame,但它的形状是由path属性来决定,且必须指定path,不然会没有形状。

CAShapeLayer的重要属性:
1、lineWidth 渲染线的宽度
2、lineCap、lineJoin 渲染线两端和转角的样式
3、fillColor、strokeColor 填充、描边的渲染颜色
4、path 指定的绘图路径,path不完整会自动封闭区域
5、strokeStart、strokeEnd 绘制path的起始和结束的百分比

CAShapeLayer的动画特点:
1、CAShapeLayer跟CALayer一样自带动画效果
2、CAShapeLayer的动画效果仅限沿路径变化,不支持填充区域的动画效果

二、动画实现
1> 固定位置的4个点

//宏定义
//整个动画View的大小
const CGFloat SURefreshHeaderHeight = 35.0;
//圆的直径
const CGFloat SURefreshPointRadius = 5.0;
//
const CGFloat SURefreshPullLen     = 85.0;
const CGFloat SURefreshTranslatLen = 5.0;

//颜色
#define topPointColor    [UIColor colorWithRed:90 / 255.0 green:200 / 255.0 blue:200 / 255.0 alpha:1.0].CGColor
#define leftPointColor   [UIColor colorWithRed:250 / 255.0 green:85 / 255.0 blue:78 / 255.0 alpha:1.0].CGColor
#define bottomPointColor [UIColor colorWithRed:92 / 255.0 green:201 / 255.0 blue:105 / 255.0 alpha:1.0].CGColor
#define rightPointColor  [UIColor colorWithRed:253 / 255.0 green:175 / 255.0 blue:75 / 255.0 alpha:1.0].CGColor

对应4个Layer,Layer的路径是圆形,填充颜色和路径颜色一致

//上
CGPoint topPoint = CGPointMake(centerLine, radius);
    self.TopPointLayer = [self layerWithPoint:topPoint color:topPointColor];
    self.TopPointLayer.hidden = NO;
    self.TopPointLayer.opacity = 0.f;
    [self.layer addSublayer:self.TopPointLayer];
//左
    CGPoint leftPoint = CGPointMake(radius, centerLine);
    self.LeftPointLayer = [self layerWithPoint:leftPoint color:leftPointColor];
    [self.layer addSublayer:self.LeftPointLayer];
//下
    CGPoint bottomPoint = CGPointMake(centerLine, SURefreshHeaderHeight - radius);
    self.BottomPointLayer = [self layerWithPoint:bottomPoint color:bottomPointColor];
    [self.layer addSublayer:self.BottomPointLayer];
//右
    CGPoint rightPoint = CGPointMake(SURefreshHeaderHeight - radius, centerLine);
    self.rightPointLayer = [self layerWithPoint:rightPoint color:rightPointColor];
    [self.layer addSublayer:self.rightPointLayer];
- (CAShapeLayer *)layerWithPoint:(CGPoint)center color:(CGColorRef)color {
    CAShapeLayer * layer = [CAShapeLayer layer];
    layer.frame = CGRectMake(center.x - SURefreshPointRadius, center.y - SURefreshPointRadius, SURefreshPointRadius * 2, SURefreshPointRadius * 2);
    layer.fillColor = color;
    layer.path = [self pointPath];
    layer.hidden = YES;
    return layer;
}

- (CGPathRef)pointPath {
    return [UIBezierPath bezierPathWithArcCenter:CGPointMake(SURefreshPointRadius, SURefreshPointRadius) radius:SURefreshPointRadius startAngle:0 endAngle:M_PI * 2 clockwise:YES].CGPath;
}

2> 4个点的连接介质

对应一个Layer,Layer的路径是由4段直线拼接而成,直线的直径和圆形的直接一致,初始的渲染结束位置为0。
8个阶段的动画,可以看成是Layer的渲染开始和结束位置不断变化,并通过改变其渲染的起始和结束位置来改变其形状

self.lineLayer = [CAShapeLayer layer];
    self.lineLayer.frame = self.bounds;
    self.lineLayer.lineWidth = SURefreshPointRadius * 2;
    self.lineLayer.lineCap = kCALineCapRound;
    self.lineLayer.lineJoin = kCALineJoinRound;
    self.lineLayer.fillColor = topPointColor;
    self.lineLayer.strokeColor = topPointColor;
    UIBezierPath * path = [UIBezierPath bezierPath];
    [path moveToPoint:topPoint];
    [path addLineToPoint:leftPoint];
    [path moveToPoint:leftPoint];
    [path addLineToPoint:bottomPoint];
    [path moveToPoint:bottomPoint];
    [path addLineToPoint:rightPoint];
    [path moveToPoint:rightPoint];
    [path addLineToPoint:topPoint];
    self.lineLayer.path = path.CGPath;
    self.lineLayer.strokeStart = 0.f;
    self.lineLayer.strokeEnd = 0.f;
    [self.layer insertSublayer:self.lineLayer above:self.TopPointLayer];

3> 通过控制progress的大小来控制动画进度

该步骤的核心是通过下拉的长度计算LineLayer的开始和结束位置,并在适当的时候显示或隐藏对应的点

//调取此方法即可实现以上动画
- (void)setLineLayerStrokeWithProgress:(CGFloat)progress {
    float startProgress = 0.f;
    float endProgress = 0.f;

    //隐藏动画
    if (progress < 0) {
        self.TopPointLayer.opacity = 0.f;
        [self adjustPointStateWithIndex:0];
    }
    //顶部的Point的可见度渐变的过程
    else if (progress >= 0 && progress < (SURefreshPullLen - 40)) {
        self.TopPointLayer.opacity = progress / 20;
        [self adjustPointStateWithIndex:0];
    }
    //开始动画,这里将进度分为4个大阶段,方便处理,请看前面的描述
    else if (progress >= (SURefreshPullLen - 40) && progress < SURefreshPullLen) {
        self.TopPointLayer.opacity = 1.0;
        //大阶段 0 ~ 3
        NSInteger stage = (progress - (SURefreshPullLen - 40)) / 10;
        //对应每个大阶段的前半段,请看前面描述
        CGFloat subProgress = (progress - (SURefreshPullLen - 40)) - (stage * 10);
        if (subProgress >= 0 && subProgress <= 5) {
            [self adjustPointStateWithIndex:stage * 2];
            startProgress = stage / 4.0;
            endProgress = stage / 4.0 + subProgress / 40.0 * 2;
        }
        //对应每个大阶段的后半段,请看前面描述
        if (subProgress > 5 && subProgress < 10) {
            [self adjustPointStateWithIndex:stage * 2 + 1];
            startProgress = stage / 4.0 + (subProgress - 5) / 40.0 * 2;
            if (startProgress < (stage + 1) / 4.0 - 0.1) {
                startProgress = (stage + 1) / 4.0 - 0.1;
            }
            endProgress = (stage + 1) / 4.0;
        }
    }
    //超过一定长度,4个点已经完全显示
    else {
        self.TopPointLayer.opacity = 1.0;
        [self adjustPointStateWithIndex:NSIntegerMax];
        startProgress = 1.0;
        endProgress = 1.0;
    }
    //计算完毕,设置LineLayer的开始和结束位置
    self.lineLayer.strokeStart = startProgress;
    self.lineLayer.strokeEnd = endProgress;
}

- (void)adjustPointStateWithIndex:(NSInteger)index { //index : 小阶段: 0 ~ 7
    self.LeftPointLayer.hidden = index > 1 ? NO : YES;
    self.BottomPointLayer.hidden = index > 3 ? NO : YES;
    self.rightPointLayer.hidden = index > 5 ? NO : YES;
    self.lineLayer.strokeColor = index > 5 ? rightPointColor : index > 3 ? bottomPointColor : index > 1 ? leftPointColor : topPointColor;
}

这里写图片描述
此动画可以拆分为两个单独动画(旋转+移动)的组合:
1)整体旋转动画:整体不断重复360度旋转
2)点反复移动动画:4个点在旋转360的周期内进行(内->外->内->外)的移动

//调取此方法 即可实现以上动画
- (void)startAni {

   [self addTranslationAniToLayer:self.TopPointLayer xValue:0 yValue:SURefreshTranslatLen];
    [self addTranslationAniToLayer:self.LeftPointLayer xValue:SURefreshTranslatLen yValue:0];
    [self addTranslationAniToLayer:self.BottomPointLayer xValue:0 yValue:-SURefreshTranslatLen];
    [self addTranslationAniToLayer:self.rightPointLayer xValue:-SURefreshTranslatLen yValue:0];
    [self addRotationAniToLayer:_view.layer];
   }
- (void)addTranslationAniToLayer:(CALayer *)layer xValue:(CGFloat)x yValue:(CGFloat)y {
    CAKeyframeAnimation * translationKeyframeAni = [CAKeyframeAnimation animationWithKeyPath:@"transform"];
    translationKeyframeAni.duration = 1.0;
    translationKeyframeAni.repeatCount = HUGE;
    translationKeyframeAni.removedOnCompletion = NO;
    translationKeyframeAni.fillMode = kCAFillModeForwards;
    translationKeyframeAni.timingFunction = [CAMediaTimingFunction functionWithName:kCAMediaTimingFunctionLinear];
    NSValue * fromValue = [NSValue valueWithCATransform3D:CATransform3DMakeTranslation(0, 0, 0.f)];
    NSValue * toValue = [NSValue valueWithCATransform3D:CATransform3DMakeTranslation(x, y, 0.f)];
    translationKeyframeAni.values = @[fromValue, toValue, fromValue, toValue, fromValue];
    [layer addAnimation:translationKeyframeAni forKey:@"translationKeyframeAni"];
}

- (void)addRotationAniToLayer:(CALayer *)layer {
    CABasicAnimation * rotationAni = [CABasicAnimation animationWithKeyPath:@"transform.rotation.z"];
    rotationAni.fromValue = @(0);
    rotationAni.toValue = @(M_PI * 2);
    rotationAni.duration = 1.0;
    rotationAni.timingFunction = [CAMediaTimingFunction functionWithName:kCAMediaTimingFunctionLinear];
    rotationAni.repeatCount = HUGE;
    rotationAni.fillMode = kCAFillModeForwards;
    rotationAni.removedOnCompletion = NO;
    [layer addAnimation:rotationAni forKey:@"rotationAni"];
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值