iOS-仿微信蛋糕雨效果CAKeyframeAnimation

2022-03-30  本文已影响0人  丿小七

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

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

上一篇下一篇

猜你喜欢

热点阅读