AnimationCircle

AnimationCircle

  • 用四条曲线拼接出小球的形状,逆时针开始,分别是弧AB,弧BC,弧CD,弧DA,每条弧都是两个弧顶点加上两个控制点,一共四个点绘制的三次贝塞尔曲线。
  • 界面布局如图所示
界面布局图.png
  • 创建继承自CALayerCircleLayer类,具体代码如下
    //  CircleLayer.h
    #import <QuartzCore/QuartzCore.h>
    @interface CircleLayer : CALayer
    /** 滑块的进度值 */
    @property(nonatomic, assign)CGFloat progress;
    @end
    
    //  CircleLayer.m
    #import "CircleLayer.h"
    #import <UIKit/UIKit.h>
    
    // 移动点的枚举
    typedef enum MovingPoint {
        POINT_D,
        POINT_B,
    } MovingPoint;
    
    // 宏定义
    #define outsideRectSize 90
    
    @interface CircleLayer()
    
    /** 外接矩形 */
    @property(nonatomic, assign)CGRect outsideRect;
    
    /** 记录上一次的progress,方便做差得出滑动方向 */
    @property(nonatomic, assign)CGFloat lastProgress;
    
    /** 实时记录滑动方向 */
    @property(nonatomic, assign)MovingPoint movePoint;
    
    @end
    
    @implementation CircleLayer
    
    // 初始化滑块的滑动值
    - (instancetype)init
    {
        self = [super init];
        if (self) {
            self.lastProgress = 0.5;
        }
        return self;
    }
    
    // 初始化动画的Layer
    - (instancetype)initWithLayer:(CircleLayer *)layer{
        self = [super initWithLayer:layer];
        if (self) {
            self.progress = layer.progress;
            self.outsideRect = layer.outsideRect;
            self.lastProgress = layer.lastProgress;
        }
        return self;
    }
    
    // 绘制小球
    -(void)drawInContext:(CGContextRef)ctx {
    
        // 外接矩形顶点和控制点的距离,当它为正方形边长的1/3.6倍时,画出来的圆弧完美贴合圆形
        CGFloat offset = self.outsideRect.size.width / 3.6;
    
        // 外接矩形顶点需要移动的距离
        CGFloat movedDistance = (self.outsideRect.size.width * 1 / 6) * fabs(self.progress - 0.5) * 2;
    
        // 外接矩形的中心点坐标
        CGPoint rectCenter = CGPointMake(self.outsideRect.origin.x + self.outsideRect.size.width / 2, self.outsideRect.origin.y + self.outsideRect.size.height / 2);
    
        // 外接矩形顶点的坐标
        CGPoint pointA = CGPointMake(rectCenter.x, self.outsideRect.origin.y + movedDistance);
        CGPoint pointB = CGPointMake(self.movePoint == POINT_D ? rectCenter.x + self.outsideRect.size.width / 2 : rectCenter.x + self.outsideRect.size.width / 2 + movedDistance * 2, rectCenter.y);
        CGPoint pointC = CGPointMake(rectCenter.x, rectCenter.y + self.outsideRect.size.height / 2 - movedDistance);
        CGPoint pointD = CGPointMake(self.movePoint == POINT_D ? self.outsideRect.origin.x - movedDistance * 2 : self.outsideRect.origin.x, rectCenter.y);
    
        // 外接矩形控制点的坐标
        CGPoint c1 = CGPointMake(pointA.x + offset, pointA.y);
        CGPoint c2 = CGPointMake(pointB.x, self.movePoint == POINT_D ? pointB.y - offset : pointB.y - offset + movedDistance);
        CGPoint c3 = CGPointMake(pointB.x, self.movePoint == POINT_D ? pointB.y + offset : pointB.y + offset -movedDistance);
        CGPoint c4 = CGPointMake(pointC.x + offset, pointC.y);
        CGPoint c5 = CGPointMake(pointC.x - offset, pointC.y);
        CGPoint c6 = CGPointMake(pointD.x, self.movePoint == POINT_D ? pointD.y + offset - movedDistance : pointD.y + offset);
        CGPoint c7 = CGPointMake(pointD.x, self.movePoint == POINT_D ? pointD.y - offset + movedDistance : pointD.y - offset);
        CGPoint c8 = CGPointMake(pointA.x - offset, pointA.y);
    
        // 绘制外接虚线矩形
        UIBezierPath *rectPath = [UIBezierPath bezierPathWithRect:self.outsideRect];
        CGContextAddPath(ctx, rectPath.CGPath);
        CGContextSetStrokeColorWithColor(ctx, [UIColor blackColor].CGColor);
        CGContextSetLineWidth(ctx, 1.0);
        CGFloat dash[] = {5.0, 5.0};
        CGContextSetLineDash(ctx, 0.0, dash, 2);
        CGContextStrokePath(ctx);
    
        // 圆的边界
        UIBezierPath *ovalPath = [UIBezierPath bezierPath];
        [ovalPath moveToPoint:pointA];
        [ovalPath addCurveToPoint:pointB controlPoint1:c1 controlPoint2:c2];
        [ovalPath addCurveToPoint:pointC controlPoint1:c3 controlPoint2:c4];
        [ovalPath addCurveToPoint:pointD controlPoint1:c5 controlPoint2:c6];
        [ovalPath addCurveToPoint:pointA controlPoint1:c7 controlPoint2:c8];
        [ovalPath closePath];
    
        CGContextAddPath(ctx, ovalPath.CGPath);
        CGContextSetStrokeColorWithColor(ctx, [UIColor blackColor].CGColor);
        CGContextSetFillColorWithColor(ctx, [UIColor redColor].CGColor);
        CGContextSetLineDash(ctx, 0, NULL, 0);
        CGContextDrawPath(ctx, kCGPathFillStroke);
    
        // 标记出每个点并连线
        CGContextSetFillColorWithColor(ctx, [UIColor yellowColor].CGColor);
        CGContextSetStrokeColorWithColor(ctx, [UIColor blackColor].CGColor);
        NSArray *points = @[[NSValue valueWithCGPoint:pointA], [NSValue valueWithCGPoint:pointB], [NSValue valueWithCGPoint:pointC], [NSValue valueWithCGPoint:pointD], [NSValue valueWithCGPoint:c1], [NSValue valueWithCGPoint:c2], [NSValue valueWithCGPoint:c3], [NSValue valueWithCGPoint:c4], [NSValue valueWithCGPoint:c5], [NSValue valueWithCGPoint:c6], [NSValue valueWithCGPoint:c7], [NSValue valueWithCGPoint:c8]];
        [self drawPoint:points withContext:ctx];
    
        // 连接辅助线
        UIBezierPath *helperline = [UIBezierPath bezierPath];
        [helperline moveToPoint:pointA];
        [helperline addLineToPoint:c1];
        [helperline addLineToPoint:c2];
        [helperline addLineToPoint:pointB];
        [helperline addLineToPoint:c3];
        [helperline addLineToPoint:c4];
        [helperline addLineToPoint:pointC];
        [helperline addLineToPoint:c5];
        [helperline addLineToPoint:c6];
        [helperline addLineToPoint:pointD];
        [helperline addLineToPoint:c7];
        [helperline addLineToPoint:c8];
        [helperline closePath];
    
        CGContextAddPath(ctx, helperline.CGPath);
    
        CGFloat dash2[] = {2.0, 2.0};
        CGContextSetLineDash(ctx, 0.0, dash2, 2);
        CGContextStrokePath(ctx);
    }
    
    // 在某个point位置画一个点,方便观察运动情况
    -(void)drawPoint:(NSArray *)points withContext:(CGContextRef)ctx {
    
        for (NSValue *pointValue in points) {
            CGPoint point = [pointValue CGPointValue];
            CGContextFillRect(ctx, CGRectMake(point.x - 2, point.y - 2, 4, 4));
        }
    }
    
    // 设置滑块的移动位置
    -(void)setProgress:(CGFloat)progress {
    
        _progress = progress;
    
        // 外接矩形在左侧,B点动
        if (progress <= 0.5) {
            self.movePoint = POINT_B;
            NSLog(@"B点动");
        } else {
            self.movePoint = POINT_D;
            NSLog(@"D点动");
        }
        self.lastProgress = progress;
    
        CGFloat origin_x = self.position.x - outsideRectSize / 2 + (progress - 0.5) * (self.frame.size.width - outsideRectSize);
        CGFloat origin_y = self.position.y - outsideRectSize / 2;
    
        self.outsideRect = CGRectMake(origin_x, origin_y, outsideRectSize, outsideRectSize);
    
        [self setNeedsDisplay];
    }
    
    @end
    
    
  • 创建继承自UIViewCircleView类,具体代码如下
    //  CircleView.h
    
    #import <UIKit/UIKit.h>
    #import "CircleLayer.h"
    
    @interface CircleView : UIView
    
    /** 圆的layer层 */
    @property(nonatomic, strong)CircleLayer *circleLayer;
    
    @end
    
    //  CircleView.m
    
    #import "CircleView.h"
    
    @implementation CircleView
    
    +(Class)layerClass {
        return [CircleLayer class];
    }
    
    -(instancetype)initWithFrame:(CGRect)frame {
        self = [super initWithFrame:frame];
        if (self) {
            self.circleLayer = [CircleLayer layer];
            self.circleLayer.frame = CGRectMake(0, 0, frame.size.width, frame.size.height);
            self.circleLayer.contentsScale = [UIScreen mainScreen].scale;
            [self.layer addSublayer:self.circleLayer];
        }
        return self;
    }
    
    @end
    
  • 控制器代码如下所示
    //  ViewController.m
    #import "ViewController.h"
    #import "CircleView.h"
    
    @interface ViewController ()
    
    /** 滑块 */
    @property (weak, nonatomic) IBOutlet UISlider *mySlider;
    /** 滑块的值 */
    @property (weak, nonatomic) IBOutlet UILabel *currentValueLabel;
    /** 圆视图 */
    @property(nonatomic, strong)CircleView *cv;
    
    @end
    
    @implementation ViewController
    
    - (void)viewDidLoad {
        [super viewDidLoad];
    
        [self.mySlider addTarget:self action:@selector(valuechange:) forControlEvents:UIControlEventValueChanged];
    
        self.cv = [[CircleView alloc] initWithFrame:CGRectMake(self.view.frame.size.width / 2 - 320 / 2, self.view.frame.size.height / 2 - 320 / 2, 320, 320)];
        [self.view addSubview:self.cv];
    
        // 首次进入
        self.cv.circleLayer.progress = _mySlider.value;
    }
    
    // 滑块值改变
    -(void)valuechange:(UISlider *)sender {
    
        self.currentValueLabel.text = [NSString stringWithFormat:@"Current: %f", sender.value];
        self.cv.circleLayer.progress = sender.value;
    }
    
    - (void)didReceiveMemoryWarning {
        [super didReceiveMemoryWarning];
        // Dispose of any resources that can be recreated.
    }
    
    @end
    
    
  • 运行结果如图所示
运行结果图.gif
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 218,284评论 6 506
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 93,115评论 3 395
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 164,614评论 0 354
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 58,671评论 1 293
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 67,699评论 6 392
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 51,562评论 1 305
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 40,309评论 3 418
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 39,223评论 0 276
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 45,668评论 1 314
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,859评论 3 336
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,981评论 1 348
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 35,705评论 5 347
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 41,310评论 3 330
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,904评论 0 22
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 33,023评论 1 270
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 48,146评论 3 370
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,933评论 2 355

推荐阅读更多精彩内容

  • 玩转贝塞尔曲线 历史:由法国雷诺汽车的工程师皮诺尔·贝塞尔发明,应用于雷诺汽车设计原理铺垫:给定 n+1个数据点,...
    百草纪阅读 1,188评论 1 9
  • #define kBlackColor [UIColor blackColor] //.h //划线 + (voi...
    CHADHEA阅读 794评论 0 1
  • Quartz2D以及drawRect的重绘机制字数1487 阅读21 评论1 喜欢1一、什么是Quartz2D Q...
    PurpleWind阅读 773评论 0 3
  • 前言 前段时间看到一个挺有趣的动画效果,于是就想着实现一下,顺便练下手,这里是第一篇教程,介绍了启动动画的制作,示...
    Arthury阅读 676评论 5 11
  • 我爱你 我不敢说 不想说给山川 也不想说给大地 却被风儿泄露了秘密 风儿知道了 全世界就知道了 我爱你
    惊涛的诗阅读 256评论 5 13