美文网首页iOS开发技术
动画| 金币抛入红包动画详解

动画| 金币抛入红包动画详解

作者: 進无尽 | 来源:发表于2018-03-19 15:41 被阅读27次

    前言

    这个动画效果很早就出来了,也是一个比较经典的关键帧动画和组合动画的运用,通过剖析源码,可以发现实际上这个酷炫的动画实现起来很简单。


    金币.gif

    实现过程

    • 在当前页面加载一个福袋的图片和再来一次的按钮。
    • 在for 循环中使用延迟调用函数。每个函数的调用时间越来越靠后,达到依次出现的效果。
    • 在每个延迟调用函数中创建一个金币的图片,并记录它的tag和最终的位置。
    • 为这个金币图片随机生成开始位置,并根据开始位置和结束位置计算出控制点,利用这三点绘制二次贝塞尔曲线。
    • 每个金币图层都执行一个动画组,一边沿轨迹做抛物线动画一遍做从大到小的3D缩放动画。
    • 每个金币执行完动画后,从图层中移除。
    • 所有金币都执行完动画后钱袋图层执行摇晃动画。

    立即打开

    //统计金币数量的变量
    static int coinCount = 0;
    - (void)getCoinAction:(UIButton *)btn
    {
        //"立即打开"按钮从视图上移除
        [btn removeFromSuperview];
        
        //初始化金币生成的数量
        coinCount = 0;
        for (int i = 0; i<kCoinCountKey; i++) {
            //延迟调用函数
            [self performSelector:@selector(initCoinViewWithInt:) withObject:[NSNumber numberWithInt:i] afterDelay:i * 0.01];
        }
    }
    

    延迟调用函数

    - (void)initCoinViewWithInt:(NSNumber *)i
    {
        UIImageView *coin = [[UIImageView alloc] initWithImage:[UIImage imageNamed:[NSString stringWithFormat:@"icon_coin_%d.png",[i intValue] % 2 + 1]]];
        
        //初始化金币的最终位置
        coin.center = CGPointMake(CGRectGetMidX(self.view.frame) + arc4random()%40 * (arc4random() %3 - 1) - 20, CGRectGetMidY(self.view.frame) - 20);
        coin.tag = [i intValue] + 1;
        NSLog(@"KKK: %lf",CGRectGetMidX(self.view.frame));
        //每生产一个金币,就把该金币对应的tag加入到数组中,用于判断当金币结束动画时和福袋交换层次关系,并从视图上移除
        [_coinTagsArr addObject:[NSNumber numberWithInteger:coin.tag]];
        
        [self.view addSubview:coin];
    
        [self setAnimationWithLayer:coin];
    }
    

    每个金币图层都需要做的动画组

    - (void)setAnimationWithLayer:(UIView *)coin
    {
        CGFloat duration = 1.6f;
        
        ////////////////////////////////////////////////////////////////////////////////////////////
        //绘制从底部到福袋口之间的抛物线
        CGFloat positionX   = coin.layer.position.x;    //终点x
        CGFloat positionY   = coin.layer.position.y;    //终点y
        
        int fromX       = arc4random() % 320;     //起始位置:x轴上随机生成一个位置
        int height      = [UIScreen mainScreen].bounds.size.height + coin.frame.size.height; //y轴以屏幕高度为准
        int fromY       = arc4random() % (int)positionY; //起始位置:生成位于福袋上方的随机一个y坐标
        
        CGFloat cpx = positionX + (fromX - positionX)/2;    //x控制点
        CGFloat cpy = fromY / 2 - positionY;                //y控制点,确保抛向的最大高度在屏幕内,并且在福袋上方(负数)
    
        CGMutablePathRef path = CGPathCreateMutable();
        //动画的起始位置
        CGPathMoveToPoint(path, NULL, fromX, height);
        CGPathAddQuadCurveToPoint(path, NULL, cpx, cpy, positionX, positionY);
    
        CAKeyframeAnimation *animation = [CAKeyframeAnimation animationWithKeyPath:@"position"];
        [animation setPath:path];
        CFRelease(path);
        path = nil;
        
        ////////////////////////////////////////////////////////////////////////////////////////////
        //图像由大到小的变化动画
        CGFloat from3DScale = 1 + arc4random() % 10 *0.1;
        CGFloat to3DScale = from3DScale * 0.5;
        CAKeyframeAnimation *scaleAnimation = [CAKeyframeAnimation animationWithKeyPath:@"transform"];
        scaleAnimation.values = @[[NSValue valueWithCATransform3D:CATransform3DMakeScale(from3DScale, from3DScale, from3DScale)], [NSValue valueWithCATransform3D:CATransform3DMakeScale(to3DScale, to3DScale, to3DScale)]];
        scaleAnimation.timingFunctions = @[[CAMediaTimingFunction functionWithName:kCAMediaTimingFunctionEaseOut], [CAMediaTimingFunction functionWithName:kCAMediaTimingFunctionEaseIn]];
    
        
        ////////////////////////////////////////////////////////////////////////////////////////////
        //动画组合
        CAAnimationGroup *group = [CAAnimationGroup animation];
        group.delegate = self;
        group.duration = duration;
        group.fillMode = kCAFillModeForwards;
        group.removedOnCompletion = NO;
        group.animations = @[scaleAnimation, animation];
        [coin.layer addAnimation:group forKey:@"position and transform"];
    }
    

    每个金币图层动画组执行完后执行

    - (void)animationDidStop:(CAAnimation *)anim finished:(BOOL)flag
    {
        if (flag) {
            //动画完成后把金币和数组对应位置上的tag移除
            UIView *coinView = (UIView *)[self.view viewWithTag:[[_coinTagsArr firstObject] intValue]];
            
            [coinView removeFromSuperview];
            [_coinTagsArr removeObjectAtIndex:0];
            
            //全部金币完成动画后执行的动作
            if (++coinCount == kCoinCountKey) {
                [self bagShakeAnimation];
                if (_getBtn) {
                    [self.view addSubview:_getBtn];
                    [_getBtn setTitle:@"再来一次" forState:UIControlStateNormal];
                }
            }
        }
    }
    

    福袋晃动动画

    - (void)bagShakeAnimation
    {
        CABasicAnimation* shake = [CABasicAnimation animationWithKeyPath:@"transform.rotation.z"];
        shake.fromValue = [NSNumber numberWithFloat:- 0.2];
        shake.toValue   = [NSNumber numberWithFloat:+ 0.2];
        shake.duration = 0.1;
        shake.autoreverses = YES;
        shake.repeatCount = 4;
        
        [_bagView.layer addAnimation:shake forKey:@"bagShakeAnimation"];
    }
    

    PS:如果我们把每一个金币图层的路径都画出来的话,会更明白图层的轨迹了:

    金币轨迹.gif

    相关文章

      网友评论

        本文标题:动画| 金币抛入红包动画详解

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