美文网首页
iOS-仿微信蛋糕雨效果CAKeyframeAnimation

iOS-仿微信蛋糕雨效果CAKeyframeAnimation

作者: 丿小七 | 来源:发表于2022-03-30 17:12 被阅读0次

    模仿微信蛋糕雨的动画效果,因为动画做的少,查了些资料,开始觉得粒子效果的动画很炫,简单做了个demo后发现他有些细节不好控制,像微信的蛋糕雨,停止的时候是慢慢停的,而粒子效果的停止跟出现,都是直接铺满或者直接全部停止的。最后还是考虑了用帧动画的形式去实现蛋糕雨的效果。简单记录下自己的使用效果跟代码片段。

    • CAKeyframeAnimation

      values 决定移动轨迹
      duration 移动轨迹需要的时长
      timingFunction 定义动画在其持续时间(或在一个关键帧的持续时间)

    • 效果


      效果参考.gif
    CAKeyframeAnimation *framMoveA = [[CAKeyframeAnimation alloc] init];
        framMoveA.keyPath = @"position";
        NSMutableArray *points = [NSMutableArray array];
        UInt32 sW = (UInt32)ceilf(self.view.frame.size.width);
        UInt32 sH = (UInt32)ceilf(self.view.frame.size.height);
    // 随机分布在屏幕内
        uint32_t randX1 = arc4random_uniform(sW);
        uint32_t randX2 = arc4random_uniform((sW*3)/4);
        uint32_t randX3 = arc4random_uniform(sW/2);
        uint32_t randX4 = arc4random_uniform(sW);
        uint32_t randX5 = arc4random_uniform((sW*3)/4);
    
        NSValue *value1 = [NSValue valueWithCGPoint:CGPointMake(randX1, 0)];
        NSValue *value2 = [NSValue valueWithCGPoint:CGPointMake(randX2, sH/4)];
        NSValue *value3 = [NSValue valueWithCGPoint:CGPointMake(randX3, sH/2)];
        NSValue *value4 = [NSValue valueWithCGPoint:CGPointMake(randX4, sH*3/4)];
        NSValue *value5 = [NSValue valueWithCGPoint:CGPointMake(randX5, sH)];
        [points addObject:value1];
        [points addObject:value2];
        [points addObject:value3];
        [points addObject:value4];
        [points addObject:value5];
        framMoveA.values = points;
        framMoveA.duration = animationTime;
        framMoveA.repeatCount = 1;
        CAMediaTimingFunction *timingFuc = [CAMediaTimingFunction functionWithName:kCAMediaTimingFunctionLinear];
        framMoveA.timingFunction = timingFuc;
        [self.moveLayer addAnimation:framMoveA forKey:@"move"];
    
    • 整体代码参考
    //
    //  ViewController.m
    //  animal
    //
    //  Created by xxx on 2022/3/29.
    //
    
    #import "ViewController.h"
    
    @interface ViewController ()
    @property (nonatomic, strong) CAEmitterLayer *emitterL; /**< 粒子动画层 */
    
    @property (nonatomic, strong) NSTimer *timer; /**< timer */
    @property (nonatomic, strong) CALayer *moveLayer; /**<   */
    @property (nonatomic, assign) NSInteger countTime; /**< 动画时长 计时结束自动停止 */
    @end
    
    #define animationTime 8 // 动画执行时间,从开始掉落到结束中间的运行时间。
    #define animationSpeed 0.5 // 执行间隔时间 0.5秒执行一次
    
    @implementation ViewController
    
    - (void)viewDidLoad {
        [super viewDidLoad];
    }
    
    - (void)viewDidAppear:(BOOL)animated {
        [super viewDidAppear:animated];
    }
    
    #pragma mark -  ============== view animation ==============
    // 定时器循环创建需要掉落的图片
    - (NSTimer *)timer {
        if (!_timer) {
            _timer = [NSTimer timerWithTimeInterval:animationSpeed target:self selector:@selector(handleTimerEvent) userInfo:nil repeats:YES];
        }
        return _timer;
    }
    
    - (void)handleTimerEvent {
        if (self.countTime < 0) {
            [self handleViewAnimationClick:nil];
        }else {
            self.countTime --;
            UIImageView *imgV = [[UIImageView alloc] initWithImage:[UIImage imageNamed:@"testImg"]];
            imgV.frame = CGRectMake(0, 0, 40, 40);
            
            self.moveLayer = [[CALayer alloc] init];
            self.moveLayer.bounds = imgV.frame;
            self.moveLayer.anchorPoint = CGPointMake(0, 0);
            self.moveLayer.position = CGPointMake(0, -40);
            self.moveLayer.contents = (__bridge id _Nullable)(imgV.image.CGImage);
            [self.view.layer addSublayer:self.moveLayer];
            
            [self handleAnimation:self.moveLayer];
        }
        
    }
    
    // 大概类似实现从上到下,整个屏幕内的随机范围掉落效果
    - (void)handleAnimation:(CALayer *)moveLayer {
        CAKeyframeAnimation *framMoveA = [[CAKeyframeAnimation alloc] init];
        framMoveA.keyPath = @"position";
        NSMutableArray *points = [NSMutableArray array];
        UInt32 sW = (UInt32)ceilf(self.view.frame.size.width);
        UInt32 sH = (UInt32)ceilf(self.view.frame.size.height);
    
        uint32_t randX1 = arc4random_uniform(sW);
        uint32_t randX2 = arc4random_uniform((sW*3)/4);
        uint32_t randX3 = arc4random_uniform(sW/2);
        uint32_t randX4 = arc4random_uniform(sW);
        uint32_t randX5 = arc4random_uniform((sW*3)/4);
    
        NSValue *value1 = [NSValue valueWithCGPoint:CGPointMake(randX1, 0)];
        NSValue *value2 = [NSValue valueWithCGPoint:CGPointMake(randX2, sH/4)];
        NSValue *value3 = [NSValue valueWithCGPoint:CGPointMake(randX3, sH/2)];
        NSValue *value4 = [NSValue valueWithCGPoint:CGPointMake(randX4, sH*3/4)];
        NSValue *value5 = [NSValue valueWithCGPoint:CGPointMake(randX5, sH)];
        [points addObject:value1];
        [points addObject:value2];
        [points addObject:value3];
        [points addObject:value4];
        [points addObject:value5];
        framMoveA.values = points;
        framMoveA.duration = animationTime;
        framMoveA.repeatCount = 1;
        CAMediaTimingFunction *timingFuc = [CAMediaTimingFunction functionWithName:kCAMediaTimingFunctionLinear];
        framMoveA.timingFunction = timingFuc;
        [self.moveLayer addAnimation:framMoveA forKey:@"move"];
    }
    
    - (IBAction)handleViewAnimationClick:(UIButton *)sender {
        NSInteger tag = sender ? sender.tag : 0;
        switch (tag) {
            case 100: // 开始动画
            {
                self.countTime = animationTime/animationSpeed;
                [[NSRunLoop mainRunLoop] addTimer:self.timer forMode:NSDefaultRunLoopMode];
                [self.timer fire];
            }
                break;
            case 101: // 结束动画
            {
                [self.timer invalidate];
                
            }
                break;
                
            default:
                [self.timer invalidate];
                self.timer = nil;
                break;
        }
        
    }
    
    
    #pragma mark -  ============== CAEmitterLayer ==============
    
    - (IBAction)stop:(UIButton *)sender {
        [self.emitterL removeAllAnimations];
        [self.emitterL removeFromSuperlayer];
    }
    
    - (IBAction)animationFirstBtnClick:(UIButton *)sender {
        [self tmyHandleInitAnimation];
    }
    
    - (void)tmyHandleInitAnimation {
        CGFloat wH = self.view.frame.size.height;
        NSMutableArray *cellArr = [NSMutableArray array];
        for (NSInteger i = 0; i< 2; i ++) {
            CAEmitterCell *cell2 = [[CAEmitterCell alloc] init];
            cell2.velocity = wH/5;
            cell2.velocityRange = (wH / 5)*I;
            cell2.scale = 2;
            cell2.scaleRange = 1;
            cell2.emissionLongitude = M_PI_2; // 经度 竖直方向
            cell2.emissionRange = M_PI_2 / 4;
            cell2.lifetime = 12;
            cell2.lifetimeRange = 0;
    //        cell2.spin = M_PI_2; // 图片是否旋转角度
    //        cell2.spinRange = M_PI_2 / 2; // 旋转角度范围
            cell2.birthRate = 1;
            cell2.contents = (__bridge id _Nullable)([UIImage imageNamed:@"testImg"].CGImage);
            
            [cellArr addObject:cell2];
        }
        self.emitterL.emitterCells = cellArr;
        
        [self.view.layer addSublayer:self.emitterL];
    }
    
    /** 粒子发射器层。
    每个发射器有一个细胞阵列,细胞定义如何粒子是由层发出和渲染的。
    粒子系统受层的时间影响。仿真开始在图层的beginTime。
    粒子绘制在背景颜色和边界层。
     */
    - (CAEmitterLayer *)emitterL {
        if (!_emitterL) {
            _emitterL = [[CAEmitterLayer alloc] init];
            _emitterL.emitterPosition = CGPointMake(self.view.center.x, -self.view.frame.size.height);
            _emitterL.preservesDepth = YES;
            _emitterL.emitterSize = self.view.frame.size;
    
    //        _emitterL.lifetime = 10;
        }
        return _emitterL;
    }
    
    

    具体 CAKeyframeAnimationCAEmitterLayer的属性可以参考以下文章。

    参考链接:
    iOS表情雨-类似微信的红包雨
    iOS --粒子效果简单实现
    CAKeyframeAnimation

    相关文章

      网友评论

          本文标题:iOS-仿微信蛋糕雨效果CAKeyframeAnimation

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