点赞彩蛋粒子发射效果




https://github.com/aliuhongliang/Emitter

模仿今日头条粒子效果CAEmitterLayer


https://github.com/Qu000/EmitterLayer

直播间粒子动画之一

https://github.com/Ccalary/EmitterDemo

粒子动画 基于CAEmitterLayer实现的下雪和烟花效果


https://github.com/SplashZ/YJFavorEmitter

https://github.com/weizhangCoder/Emitterabel

CAEmitterLayer常用属性

https://www.jianshu.com/p/d02355dd18d8

https://github.com/Joethermal/CloudEmitter


仿真动画UIDynamicAnimatorUIGravityBehaviorUICollisionBehavior
https://github.com/JnKindle/UIDynamicAnimator


PAG现已开源
Github地址
https://github.com/Tencent/libpag

https://pag.art

关键帧动画

https://github.com/shmxybfq/TFDemos/tree/525c377706c84ff53e2afe906d8a528f1471a523

iOS UIKit动力学推动UIPushBehavior

https://www.jianshu.com/p/caa5461e0917



仿今日头条点赞喷射表情动画

https://github.com/LifeForLove/GXUpvoteButton




//
//  GXUpvoteButton.m
//
//  Created by apple on 2017/12/19.
//  Copyright © 2017年 getElementByYou. All rights reserved.
//

#import "GXUpvoteButton.h"

@interface GXUpvoteButton()<CAAnimationDelegate>

@property (nonatomic,strong) NSMutableArray *imgArr;

@property (nonatomic,strong) CAEmitterLayer * longPressLayer;

@property (nonatomic,strong) UILongPressGestureRecognizer *longPressGes;

//Core Animation
@property (nonatomic, strong) UIImageView *caStarImageView;
@property (nonatomic, strong) CAEmitterLayer *caStarEmitter;

@property (nonatomic, assign) BOOL shouldShootOffCoreAnimationStar;
@property (nonatomic, strong) UIButton *curBtn;

@end



@implementation GXUpvoteButton {
    NSTimer *_timer; //定时器
}

- (instancetype)initWithFrame:(CGRect)frame {
    self = [super initWithFrame:frame];
    if (self) {
        [self setup];
    }
    return self;
}

- (instancetype)initWithCoder:(NSCoder *)coder {
    self = [super initWithCoder:coder];
    if (self) {
        [self setup];
    }
    return self;
}

/**
 *  配置WclEmitterButton
 */
- (void)setup {
    //添加点击事件
    //点一下
    [self addTarget:self action:@selector(pressOnece:) forControlEvents:UIControlEventTouchUpInside];
    //长按
    self.longPressGes = [[UILongPressGestureRecognizer alloc]initWithTarget:self action:@selector(longPress:)];
    [self addGestureRecognizer:self.longPressGes];
    
    [self setImage:[UIImage imageNamed:@"feed_like"] forState:UIControlStateNormal];
    [self setImage:[UIImage imageNamed:@"feed_like_press"] forState:UIControlStateSelected];
}

/**
 点了一下
 */
- (void)pressOnece:(UIButton *)sender {
    if ([self.delegate respondsToSelector:@selector(upvoteBtnTouchUpInsideAction:)]) {
        [self.delegate upvoteBtnTouchUpInsideAction:sender];
    }
    
    //如果快速点赞
    if ([self.imageView.layer animationForKey:@"transform.scale"] && sender.selected) {
        
        [self tapAnimation];
    } else {
        sender.selected = !sender.selected;
        if (sender.selected) {
            
            [self tapAnimation];
        } else {
            
        }
    }
}

/**
 点按动画
 */
- (void)tapAnimation {
    [self handimgAnimation];
    CAEmitterLayer * layer = [self createStreamLayer];
    [self upvoteShowWithStreamLayer:layer];
    [self upvoteHiddenWithStreamLayer:layer];
    [self feedBackGeneratorAction];
}

/**
 振动
 */
- (void)feedBackGeneratorAction {
    if (@available(iOS 10.0, *)) {
        UIImpactFeedbackGenerator * impactLight = [[UIImpactFeedbackGenerator alloc]initWithStyle:UIImpactFeedbackStyleHeavy];
        [impactLight impactOccurred];
    } else {
        // Fallback on earlier versions
    }
}

/**
 点赞图片动画
 */
- (void)handimgAnimation {
    CAKeyframeAnimation *animation = [CAKeyframeAnimation animationWithKeyPath:@"transform.scale"];
    if (self.selected) {
        animation.values = @[@1.5 ,@0.8, @1.0,@1.2,@1.0];
        animation.duration = 0.5;
    }else
    {
        animation.values = @[@0.8, @1.0];
        animation.duration = 0.4;
    }
    animation.calculationMode = kCAAnimationCubic;
    animation.delegate = self;
    [self.imageView.layer addAnimation:animation forKey:@"transform.scale"];
}

/**
 长按
 
 @param ges 手势
 */
- (void)longPress:(UIGestureRecognizer *)ges {
    
    UIButton * sender = (UIButton *)ges.view;
    sender.selected = YES;
    
    if (ges.state == UIGestureRecognizerStateBegan) {
        _curBtn = sender;
        self.shouldShootOffCoreAnimationStar = YES;
        _timer = [NSTimer scheduledTimerWithTimeInterval:0.1 target:self selector:@selector(shootOffCoreAnimation) userInfo:nil repeats:YES];
    } else if (ges.state == UIGestureRecognizerStateEnded || ges.state == UIGestureRecognizerStateCancelled) {
        self.shouldShootOffCoreAnimationStar = NO;
        [_timer invalidate];
        _timer = nil;
    }
    
}

- (void)shootOffCoreAnimation{
    // 在这里编写你希望执行的代码
    [self shootOffCoreAnimationStarFromView:_curBtn];
    [self longPressTimerAction];
}

/**
 定时器事件
 */
- (void)longPressTimerAction {
    [self feedBackGeneratorAction];
}

- (void)upvoteShowWithStreamLayer:(CAEmitterLayer *)streamerLayer  {
    [self createEmitterCellArr:self.imgArr streamerLayer:streamerLayer];
    for (NSString * imageStr in self.imgArr) {
        [streamerLayer setValue:[NSNumber numberWithInteger:5] forKeyPath:[NSString stringWithFormat:@"emitterCells.%@.birthRate",imageStr]];
    }
}

- (void)upvoteHiddenWithStreamLayer:(CAEmitterLayer *)streamerLayer {
    dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(0.2 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
        for (NSString * imageStr in self.imgArr) {
            [streamerLayer setValue:[NSNumber numberWithInteger:0] forKeyPath:[NSString stringWithFormat:@"emitterCells.%@.birthRate",imageStr]];
        }
        dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(0.6 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
            [streamerLayer removeFromSuperlayer];
        });
    });
}

- (void)removeLongPressLayer {
    dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(0.2 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
        for (NSString * imageStr in self.imgArr) {
            [self.longPressLayer setValue:[NSNumber numberWithInteger:0] forKeyPath:[NSString stringWithFormat:@"emitterCells.%@.birthRate",imageStr]];
        }
        dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(0.6 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
            [self.longPressLayer removeFromSuperlayer];
            if (self.longPressLayer) {
                self.longPressLayer = nil;
                self.longPressGes.enabled = YES;
            }
        });
    });
    
}


- (CAEmitterLayer *)createStreamLayer {
    //设置暂时的layer
    CAEmitterLayer *streamerLayer = [CAEmitterLayer layer];
    streamerLayer.position = CGPointMake(self.frame.size.width/2.0, self.frame.size.height/2.0);
    //发射器的尺寸
    streamerLayer.emitterSize   = self.layer.bounds.size;
    streamerLayer.masksToBounds = NO;
    streamerLayer.renderMode = kCAEmitterLayerBackToFront;
    return streamerLayer;
}

- (NSMutableArray *)imgArr {
    if (_imgArr == nil) {
        _imgArr = [NSMutableArray array];
        for (int i = 1; i < 8; i++) {
            //78张图片 随机选9张
            int x = arc4random() % 77 + 1;
            NSString * imageStr = [NSString stringWithFormat:@"emoji_%d",x];
            [_imgArr addObject:imageStr];
        }
    }
    return _imgArr;
}

/**
 创建粒子单元数组
 */
- (NSMutableArray *)createEmitterCellArr:(NSMutableArray *)imgArr streamerLayer:(CAEmitterLayer *)streamerLayer {
    //设置展示的cell
    NSMutableArray * emitterCellArr = [NSMutableArray array];
    for (NSString * imageStr in imgArr) {
        CAEmitterCell * cell = [self emitterCell:[UIImage imageNamed:imageStr] Name:imageStr];
        [emitterCellArr addObject:cell];
    }
    streamerLayer.emitterCells  = emitterCellArr;
    [self.layer addSublayer:streamerLayer];
    return emitterCellArr;
}

/**
 *  开始动画
 */
- (void)animation {
    CAKeyframeAnimation *animation = [CAKeyframeAnimation animationWithKeyPath:@"transform.scale"];
    animation.values = @[@1.5 ,@0.8, @1.0,@1.2,@1.0];
    animation.duration = 0.5;
    animation.calculationMode = kCAAnimationCubic;
    [self.layer addAnimation:animation forKey:@"transform.scale"];
}
/**
 创建发射的表情cell
 
 @param image 传入随机的图片
 @param name 图片的名字
 @return cell
 */
- (CAEmitterCell *)emitterCell:(UIImage *)image Name:(NSString *)name {
    CAEmitterCell * smoke = [CAEmitterCell emitterCell];
    smoke.birthRate = 0;//每秒创建的发射对象个数
    smoke.lifetime = 5;// 粒子的存活时间
    smoke.lifetimeRange = 5;
    smoke.scale = 0.5;//设置粒子的大小
    
    smoke.alphaRange = 1;
    smoke.alphaSpeed = -1.5;
    smoke.yAcceleration = 4000;//可以有下落的效果
    //    smoke.zAcceleration = 4000;
    CGImageRef image2 = image.CGImage;
    smoke.contents= (__bridge id _Nullable)(image2);
    smoke.name = name; //设置这个 用来展示喷射动画 和隐藏
    
    smoke.velocity = 1500;//速度
    smoke.velocityRange = 1500;// 平均速度
    smoke.emissionRange = M_PI_2;//粒子的发散范围
    smoke.emissionLongitude = 3 * M_PI / 2 ;
    //    smoke.spin = M_PI * 2; // 粒子的平均旋转速度
    //    smoke.spinRange = M_PI * 2;// 粒子的旋转速度调整范围
    return smoke;
}



// Core Animation Example
- (void)shootOffCoreAnimationStarFromView:(UIView *)view {
    UIWindow *window = [UIApplication sharedApplication].keyWindow;
    
    CGRect viewFrameInWindow = [view convertRect:window.bounds toView:nil];
    
    int x = arc4random() % 77 + 1;
    NSString * imageStr = [NSString stringWithFormat:@"emoji_%d",x];
    self.caStarImageView = [[UIImageView alloc] initWithImage:[UIImage imageNamed:imageStr]];
    self.caStarImageView.alpha = 1.0f;
    
    CGRect imageFrame = CGRectMake(viewFrameInWindow.origin.x, viewFrameInWindow.origin.y, 50, 50);
    
    //Your image frame.origin from where the animation need to get start
    CGPoint viewOrigin = viewFrameInWindow.origin;
    
    self.caStarImageView.frame = imageFrame;
    self.caStarImageView.layer.position = viewOrigin;
    
    [window addSubview:self.caStarImageView];
    
    // particles emitters
    self.caStarEmitter = [CAEmitterLayer new];
    self.caStarEmitter.emitterPosition = CGPointMake((self.caStarImageView.frame.size.width / 2) - 5, self.caStarImageView.frame.size.height);
    self.caStarEmitter.emitterShape = kCAEmitterLayerLine;
    self.caStarEmitter.emitterZPosition = 10; // 3;
    self.caStarEmitter.emitterSize = CGSizeMake(0.5, 0.5);
    self.caStarEmitter.lifetime = 3;
    self.caStarEmitter.birthRate = 0.5;

    [self.caStarImageView.layer addSublayer:self.caStarEmitter];
    
    // Set up fade out effect
    CABasicAnimation *fadeOutAnimation = [CABasicAnimation animationWithKeyPath:@"opacity"];
    [fadeOutAnimation setToValue:[NSNumber numberWithFloat:1.0]];
    fadeOutAnimation.fillMode = kCAFillModeForwards;
    fadeOutAnimation.removedOnCompletion = NO;
    
    // Set up path movement
    CAKeyframeAnimation *pathAnimation = [CAKeyframeAnimation animationWithKeyPath:@"position"];
    pathAnimation.calculationMode = kCAAnimationCubicPaced;
    pathAnimation.fillMode = kCAFillModeForwards;
    pathAnimation.removedOnCompletion = NO;
    pathAnimation.rotationMode = kCAAnimationRotateAuto;
    
    UIBezierPath *path = [UIBezierPath bezierPath];
    
    [path moveToPoint:viewOrigin];
  //  [path addLineToPoint:CGPointMake(0, (viewOrigin.y-100)<0 ? 30 : viewOrigin.y-100)];
    
    for (int i = 0; i < 5; i++) {
        CGFloat randomX = arc4random_uniform([[UIScreen mainScreen] bounds].size.width);
        CGFloat randomY = arc4random_uniform([[UIScreen mainScreen] bounds].size.height);
        [path addLineToPoint:CGPointMake(randomX, randomY)];
    }
    
 //   [path addLineToPoint:CGPointMake([[UIScreen mainScreen] bounds].size.width, 0)];
    
    for (int i = 0; i < 4; i++) {
        CGFloat randomX = arc4random_uniform([[UIScreen mainScreen] bounds].size.width);
        CGFloat randomY = arc4random_uniform([[UIScreen mainScreen] bounds].size.height);
        [path addLineToPoint:CGPointMake(randomX, randomY)];
    }
    
    
    for (int i = 0; i < 3; i++) {
        CGFloat randomX = arc4random_uniform([[UIScreen mainScreen] bounds].size.width);
        CGFloat randomY = arc4random_uniform([[UIScreen mainScreen] bounds].size.height);
        [path addLineToPoint:CGPointMake(randomX, randomY)];
    }
    
    CGFloat randomX = arc4random_uniform([[UIScreen mainScreen] bounds].size.width); // Generate a random X coordinate
    [path addLineToPoint:CGPointMake(randomX, [[UIScreen mainScreen] bounds].size.height + 100)];
    
    pathAnimation.path = path.CGPath;
    CAAnimationGroup *group = [CAAnimationGroup animation];
    group.fillMode = kCAFillModeForwards;
    group.removedOnCompletion = NO;
    [group setAnimations:[NSArray arrayWithObjects:fadeOutAnimation, pathAnimation, nil]];
    group.duration = 3.0f;
    group.delegate = self;
    [group setValue:self.caStarImageView forKey:@"imageViewBeingAnimated"];
    
    [self.caStarImageView.layer addAnimation:group forKey:@"savingAnimation"];
}

- (CGFloat)randFloatBetween:(float)low and:(float)high {
    float diff = high - low;
    return (((float) rand() / RAND_MAX) * diff) + low;
}

#pragma mark - CAAnimationGroup Delegate
- (void)animationDidStop:(CAAnimation *)animation finished:(BOOL)finished {
    if (finished) {

    }
}


@end






Loading Disqus comments...
Table of Contents