绘图与动画之使用自定义属性与Quartz实现扇形加载动画

前言

许久不更博客,心痒难耐。
正好Quartz内容也有些忘却,索性写个绘图动画熟熟手。
之前见过一下扇形加载动画,具体哪个应用已经不记得,当时觉得很有意思,正好在这里实现一番。大概效果如下:

这里写图片描述

思路与算法

从图中不难看出,整个动画每转一圈会前进一部分,循环往复。
将每次旋转单独拆分的话,又分为三个部分:

  1. 扇形扩展
  2. 整体前进
  3. 扇形收缩

于是我们可以得出:整个扇形为一个以时间为未知数的周期函数,同时也是一个分段函数。
我们可以将圆分为12格,单位时间前进一格,每次旋转后整体前进1格,则循环长度为13格。
设扇形整体为8格,则循环一周需要整体前进12-8=4格,那么一次循环就需要整体前进13-8=5格。于是,一次循环总格数为8+5+8=21格。
那么,分段函数的两个节点就是8(扇形长度),13(循环长度)。

代码实现

创建一个包含时间属性的CALayer子类。时间需要定义为双精度或浮点型,若定义整型会造成断断续续的感觉。

@interface IPZArcLayer : CALayer

@property CGFloat time;

@end

实现动画过程

#import "IPZArcLayer.h"

#define IPZOriginAngle -M_PI_2

@implementation IPZArcLayer

@dynamic time;

+(BOOL)needsDisplayForKey:(NSString *)key{
    if ([@"time" isEqualToString:key]) {
        return true;
    }
    return [super needsDisplayForKey:key];
}

- (id<CAAction>)actionForKey:(NSString *)key {
    if ([key isEqualToString:@"time"]) {
        CABasicAnimation *animation = [CABasicAnimation animationWithKeyPath:key];
        animation.timingFunction = [CAMediaTimingFunction functionWithName:kCAMediaTimingFunctionLinear];
        animation.fromValue = @([[self presentationLayer] time]);                   
        return animation;
    }
    return [super actionForKey:key];
}

-(void)drawInContext:(CGContextRef)ctx{
    CGFloat time = [[self presentationLayer] time];
    CGFloat originAngle=IPZOriginAngle+ (int)(time/21)*M_PI*2/12;

    CGFloat startAngle=0;
    CGFloat endAngle=0;
    CGFloat period=[self getYushuByA:time andB:21];
    if (period <8 ) {
        startAngle=originAngle;
        endAngle=originAngle+period*2*M_PI/12;
    }else if (period<13){
        startAngle=originAngle+[self getYushuByA:period andB:8]*2*M_PI/12;
        endAngle=originAngle+period*2*M_PI/12;
    }else{
         startAngle=originAngle+([self getYushuByA:period andB:13]+5)*2*M_PI/12;
         endAngle=originAngle+M_PI*2/12;
    }

    CGContextSetRGBFillColor(ctx, 0/255.0, 165/255.0, 221/255.0, 1);

    CGContextBeginPath(ctx);
    CGContextMoveToPoint(ctx, CGRectGetMidX(self.bounds) , CGRectGetMidY(self.bounds));

    CGContextAddArc(ctx, CGRectGetMidX(self.bounds) , CGRectGetMidY(self.bounds) , 50, startAngle,endAngle , 0);
    CGContextClosePath(ctx);
    CGContextFillPath(ctx);
}

///双精度取余数
- (CGFloat)getYushuByA:(CGFloat)beichushu andB:(int)chushu{
    int bs=(int)(beichushu)/chushu;
    CGFloat yushu=beichushu-bs*chushu;
    return yushu;
}

@end

在UIView中添加IPZArcLayer,并通过间断赋值实现动画效果。

-(void)awakeFromNib{
    [super awakeFromNib];
    IPZArcLayer *layer=[IPZArcLayer new];
    layer.frame=CGRectMake(0, 0, 200, 200);
    [self.layer addSublayer:layer];
    _arcLayer=layer;
    [NSTimer scheduledTimerWithTimeInterval:0.1 target:self selector:@selector(updateProgress:) userInfo:nil repeats:true];
}

- (void)updateProgress:(id)sender{
    _time++;
    _arcLayer.time=_time;
}
  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值