美文网首页
iOS-爱奇艺、以及QQ下拉和QQ邮箱的下拉加载动画

iOS-爱奇艺、以及QQ下拉和QQ邮箱的下拉加载动画

作者: 好治愈 | 来源:发表于2018-11-13 15:21 被阅读0次

    1. 爱奇艺网络加载动画。

    首先我们先看一下,像爱奇艺这种网络加载动画,仔细的看一下其实也不是很难。


    aiqiyi.gif

    可以看成是两个部分:一部分是外面的残缺的圆环,一部分是里面的三角形。

    先是外面部分顺时针画了一个圆,然后再慢慢的消失,消失的过程中呢,里面的三角形同时也旋转。思路有了之后呢,我们来写代码:

        UIColor *color = [UIColor colorWithRed:64/255.f green:168/255.f blue:59/255.f alpha:1];
        
        CGFloat width = 70;
        CGFloat height = 70;
        UIBezierPath *path1 = [UIBezierPath bezierPathWithOvalInRect:CGRectMake((CGRectGetWidth(self.view.bounds)-width)/2, (CGRectGetHeight(self.view.bounds)-height)/2, width, height)];
        
        CAShapeLayer *layer1 = [CAShapeLayer layer];
        layer1.contentsScale = [[UIScreen mainScreen] scale];
        layer1.frame = self.view.bounds;
        layer1.path = path1.CGPath;
        layer1.strokeColor = color.CGColor;
        layer1.fillColor = [UIColor clearColor].CGColor;
        layer1.lineCap = kCALineCapRound;
        layer1.lineWidth = 7;
        layer1.transform = CATransform3DMakeRotation(-M_PI_2, 0, 0, 1);
        layer1.strokeStart = 0;
        layer1.strokeEnd = 0;
    
        [self.view.layer addSublayer:layer1];
        
        CGFloat offset = 20;
        CGPoint one = CGPointMake(CGRectGetMidX(self.view.bounds)+offset, self.view.center.y);
        CGPoint two = CGPointMake(one.x-offset/2*3, one.y+offset/2/tan(M_PI_2/3));
        CGPoint three = CGPointMake(one.x-offset/2*3, one.y-offset/2/tan(M_PI_2/3));
        
        UIBezierPath *path2 = [UIBezierPath bezierPath];
        [path2 moveToPoint:one];
        [path2 addLineToPoint:two];
        [path2 addLineToPoint:three];
        [path2 closePath];
        
        CAShapeLayer *layer2 = [CAShapeLayer layer];
        layer2.contentsScale = [[UIScreen mainScreen] scale];
        layer2.frame = self.view.bounds;
        layer2.path = path2.CGPath;
        layer2.strokeColor = color.CGColor;
        layer2.fillColor = color.CGColor;
        layer2.lineWidth = 2;
        layer2.lineCap = kCALineCapRound;
        
        [self.view.layer addSublayer:layer2];
    

    首先呢,layer1是我所绘制的残缺的圆环,layer2是里面的三角形。具体点的坐标呢,自己算吧。

    我们画完了之后呢,下一步就是让它动起来。

    这个动画比较简单,我就直接上代码了:

        __block BOOL show = YES;
        dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(1 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{    
            dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
            self->timer = dispatch_source_create(DISPATCH_SOURCE_TYPE_TIMER, 0, 0, queue);
            dispatch_source_set_timer(self->timer, DISPATCH_TIME_NOW, 0.6 * NSEC_PER_SEC, 0 * NSEC_PER_SEC);
            dispatch_source_set_event_handler(self->timer, ^{
                dispatch_async(dispatch_get_main_queue(), ^{
                    if (show) {
                        layer1.strokeStart = 0.02;
                        layer1.strokeEnd = 0.98;
                        CABasicAnimation *animation = [CABasicAnimation animationWithKeyPath:@"strokeEnd"];
                        animation.duration = 0.6;
                        animation.fromValue = @(0.02);
                        animation.toValue = @(0.98);
                        animation.timingFunction = [CAMediaTimingFunction functionWithName:kCAMediaTimingFunctionEaseInEaseOut];
                        [layer1 addAnimation:animation forKey:@"strokeEnd"];
                    } else {
                        layer1.strokeStart = 0.98;
                        layer1.strokeEnd = 0.98;
                        CABasicAnimation *animation1 = [CABasicAnimation animationWithKeyPath:@"strokeStart"];
                        animation1.duration = 0.6;
                        animation1.fromValue = @(0.02);
                        animation1.toValue = @(0.98);
                        animation1.timingFunction = [CAMediaTimingFunction functionWithName:kCAMediaTimingFunctionEaseInEaseOut];
                        [layer1 addAnimation:animation1 forKey:@"strokeStart"];
                        
                        CGFloat rotation = (M_PI/3*2)*2;
                        layer2.transform = CATransform3DRotate(layer2.transform, rotation, 0, 0, 1);
                        CABasicAnimation *animation2 = [CABasicAnimation animationWithKeyPath:@"transform.rotation.z"];
                        animation2.duration = 0.6;
                        animation2.fromValue = @(0);
                        animation2.toValue = @(rotation);
                        animation2.timingFunction = [CAMediaTimingFunction functionWithName:kCAMediaTimingFunctionEaseInEaseOut];
                        [layer2 addAnimation:animation2 forKey:@"transform.rotation.z"];
                    }
                    show = !show;
                });
            });
            dispatch_resume(self->timer);
        });
    

    2. QQ下拉和QQ邮箱下拉加载动画

    先看一下效果图:


    qqanimation.gif

    先上代码吧,下边那个红黄绿三个球球我们最后再说,我们先说QQ的下拉动画吧:

        UIColor *color = [UIColor colorWithRed:101/255.f green:200/255.f blue:250/255.f alpha:1];
        
        layer = [CAShapeLayer layer];
        layer.strokeColor = color.CGColor;
        layer.fillColor = color.CGColor;
        
        UIBezierPath *path = [UIBezierPath bezierPath];
        [path addArcWithCenter:CGPointMake(CGRectGetWidth(self.view.bounds)/2, 40) radius:15 startAngle:M_PI_2*3 endAngle:M_PI_2*7 clockwise:YES];
        layer.path = path.CGPath;
        [self.view.layer addSublayer:layer];
    

    我没有加在tableView的头上,只是通过手势的滑动来拉升的,和上面一样,先画出一个球球

    通过手势来触发的方法如下:

    - (void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event {
        [super touchesBegan:touches withEvent:event];
        
        UITouch *touch = [touches anyObject];
        startPoint = [touch locationInView:self.view];
    }
    
    - (void)touchesMoved:(NSSet<UITouch *> *)touches withEvent:(nullable UIEvent *)event {
        [super touchesMoved:touches withEvent:event];
        
        UITouch *touch = [touches anyObject];
        CGPoint point = [touch locationInView:self.view];
        
        if (point.y < startPoint.y) {
            UIBezierPath *path = [UIBezierPath bezierPath];
            [path addArcWithCenter:CGPointMake(CGRectGetWidth(self.view.bounds)/2, 40) radius:15 startAngle:M_PI_2*3 endAngle:M_PI_2*7 clockwise:YES];
            layer.path = path.CGPath;
            return;
        } else {
            CGFloat s =  point.y-startPoint.y;
            
            CGPoint topCenter = CGPointMake(CGRectGetWidth(self.view.bounds)/2, 40);
            CGPoint rightPathC = CGPointMake(topCenter.x+(15-3-s/6), topCenter.y+s/2); 
            CGPoint bottomCenter = CGPointMake(topCenter.x, topCenter.y+s);
            CGPoint leftPathC = CGPointMake(topCenter.x-(15-3-s/6), topCenter.y+s/2); 
            
            UIBezierPath *path = [UIBezierPath bezierPath];
            [path addArcWithCenter:topCenter radius:15-s/6 startAngle:M_PI_2*2 endAngle:M_PI_2*4 clockwise:YES];
            [path addQuadCurveToPoint:CGPointMake(rightPathC.x, topCenter.y+s) controlPoint:rightPathC];
            [path addArcWithCenter:bottomCenter radius:15-3-s/6 startAngle:M_PI_2*4 endAngle:M_PI_2*6 clockwise:YES];
            [path addQuadCurveToPoint:CGPointMake(leftPathC.x-3, topCenter.y) controlPoint:leftPathC];
            [path closePath];
            
            layer.path = path.CGPath;
        }
    }
    

    我们先判断手势的滑动方向,刚接触的时候记录一下手势的起始位置,如果方向向下,我们就继续,否则就不处理动画效果。

    动画开始的时候,我们通过滑动的位移来分别计算四个点:
    它们分别是上边的圆的圆心点topCenter、右边的贝塞尔曲线的控制点rightPathC、下边的圆的圆心点bottomCenter和左边的贝塞尔曲线的控制点leftPathC如下图:

    qq.png
    其实只要算对了坐标,这个还是很好弄的。

    接下来我们说说那三个球球,那个动画我也弄了好久,因为我当时观察动画的时候,也看了好久,因为太快了,容易看乱,所以后来我录了个视频,放慢了来看,才看清楚那个动画。

    三个球看着费力,我们先看一个球,我们就拿红球位置来分析:
    我们可以分段来看它的运动,通过分析我们可以把红球看作运动了6次


    red_IMG.png

    如上图所示:

    红球一共运动了六个阶段,其中4、5阶段没有位移,处于静止状态

    下面我们用代码来写这个逻辑,正方向我们用 +1 来代替,反方向用 -1 来代替,静止我们就用 0 来表示,我们以中间位置为起点先写一个有六个阶段位移的数组:

        NSArray *six = @[@(-1), @(1), @(1), @(-1), @(0), @(0)];
    

    如果以中间位置为起点,那么红球的第一次运动为第一阶段

    那黄球和绿球也一样,只不过它们每个球的第一次运动在不同的阶段,黄球在第五阶段,绿球在第三阶段。

    下面我们让它们动起来,我是以定时器让它跑起来的,废话不多说了,上代码:

        float opacity = 0.8;
        CGPoint center = CGPointMake(CGRectGetWidth(self.view.bounds)/2, CGRectGetHeight(self.view.bounds)/2);
        __block CGFloat radius = 6;
        __block CGFloat offset = 30;
        
        CAShapeLayer *red = [CAShapeLayer layer];
        red.strokeColor = [UIColor redColor].CGColor;
        red.fillColor = [UIColor redColor].CGColor;
        red.opacity = opacity;
        
        UIBezierPath *redPath = [UIBezierPath bezierPath];
        __block CGPoint redCenter = CGPointMake(center.x-offset, center.y);
        [redPath addArcWithCenter:redCenter radius:radius startAngle:M_PI_2*3 endAngle:M_PI_2*7 clockwise:YES];
        red.path = redPath.CGPath;
        [self.view.layer addSublayer:red];
        
        CAShapeLayer *yellow = [CAShapeLayer layer];
        yellow.strokeColor = [UIColor yellowColor].CGColor;
        yellow.fillColor = [UIColor yellowColor].CGColor;
        yellow.opacity = opacity;
    
        UIBezierPath *yellowPath = [UIBezierPath bezierPath];
        __block CGPoint yellowCenter = center;
        [yellowPath addArcWithCenter:yellowCenter radius:radius startAngle:M_PI_2*3 endAngle:M_PI_2*7 clockwise:YES];
        yellow.path = yellowPath.CGPath;
        [self.view.layer addSublayer:yellow];
        
        CAShapeLayer *green = [CAShapeLayer layer];
        green.strokeColor = [UIColor greenColor].CGColor;
        green.fillColor = [UIColor greenColor].CGColor;
        green.opacity = opacity;
    
        UIBezierPath *greenPath = [UIBezierPath bezierPath];
        __block CGPoint greenCenter = CGPointMake(center.x+offset, center.y);
        [greenPath addArcWithCenter:greenCenter radius:radius startAngle:M_PI_2*3 endAngle:M_PI_2*7 clockwise:YES];
        green.path = greenPath.CGPath;
        [self.view.layer addSublayer:green];
        
        NSArray *six = @[@(-1), @(1), @(1), @(-1), @(0), @(0)];
            
        __block NSInteger redStaIdx = 1;
        __block NSInteger yellowStaIdx = 5;
        __block NSInteger greenStaIdx = 3;
    
        __block NSInteger timeCount = 0; 
        CGFloat time = 0.008;
        dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
        timer = dispatch_source_create(DISPATCH_SOURCE_TYPE_TIMER, 0, 0, queue);
        dispatch_source_set_timer(timer, DISPATCH_TIME_NOW, time * NSEC_PER_SEC, 0 * NSEC_PER_SEC);
        dispatch_source_set_event_handler(timer, ^{   
            dispatch_async(dispatch_get_main_queue(), ^{   
                NSInteger redV = ((NSNumber *)six[redStaIdx]).integerValue;
                NSInteger yellowV = ((NSNumber *)six[yellowStaIdx]).integerValue;
                NSInteger greenV = ((NSNumber *)six[greenStaIdx]).integerValue;
                
                if (redStaIdx == 3) {
                    red.zPosition = 3;
                    yellow.zPosition = 2;
                    green.zPosition = 1;
                } 
                if (yellowStaIdx == 3) {
                    red.zPosition = 1;
                    yellow.zPosition = 3;
                    green.zPosition = 2;  
                }
                if (greenStaIdx == 3) {
                    red.zPosition = 2;
                    yellow.zPosition = 1;
                    green.zPosition = 3;
                }
                
                UIBezierPath *redPath = [UIBezierPath bezierPath];
                redCenter = CGPointMake(redCenter.x+1*redV, redCenter.y);
                [redPath addArcWithCenter:redCenter radius:radius startAngle:M_PI_2*3 endAngle:M_PI_2*7 clockwise:YES];
                red.path = redPath.CGPath;
                
                UIBezierPath *yellowPath = [UIBezierPath bezierPath];
                yellowCenter = CGPointMake(yellowCenter.x+1*yellowV, yellowCenter.y);
                [yellowPath addArcWithCenter:yellowCenter radius:radius startAngle:M_PI_2*3 endAngle:M_PI_2*7 clockwise:YES];
                yellow.path = yellowPath.CGPath;
                
                UIBezierPath *greenPath = [UIBezierPath bezierPath];
                greenCenter = CGPointMake(greenCenter.x+1*greenV, greenCenter.y);
                [greenPath addArcWithCenter:greenCenter radius:radius startAngle:M_PI_2*3 endAngle:M_PI_2*7 clockwise:YES];
                green.path = greenPath.CGPath;
    
                timeCount++;
                
                if (timeCount == offset) {
                    timeCount = 0;
                    
                    redStaIdx++;                
                    if (redStaIdx == 6) {
                        redStaIdx = 0;
                    }
                    
                    yellowStaIdx++;
                    if (yellowStaIdx == 6) {
                        yellowStaIdx = 0;
                    }
                    
                    greenStaIdx++;
                    if (greenStaIdx == 6) {
                        greenStaIdx = 0;
                    }
                }
            });
        });
        dispatch_resume(timer);
    

    这是以上的核心代码,到这儿已经可以动起来了。

    本人也只是分享一些自己的逻辑想法,动画的实现有好多方法,最简单的,找UI要一个GIF图片加上去,一切都搞定了,不多说了,看代码吧!!!

    相关文章

      网友评论

          本文标题:iOS-爱奇艺、以及QQ下拉和QQ邮箱的下拉加载动画

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