动画示例(六) —— QuartzCore之CAEmitterLayer、CAReplicatorLayer和CAGradientLayer简单动画

版本记录

版本号 时间
V1.0 2017.12.13

前言

如果你细看了我前面写的有关动画的部分,就知道前面介绍了CoreAnimation、序列帧以及LOTAnimation等很多动画方式,接下来几篇我们就以动画示例为线索,进行动画的讲解。相关代码已经上传至GitHub - 刀客传奇。感兴趣的可以看我写的前面几篇。
1. 动画示例(一) —— 一种外扩的简单动画
2. 动画示例(二) —— 一种抖动的简单动画
3. 动画示例(三) —— 仿头条一种LOTAnimation动画
4. 动画示例(四) —— QuartzCore之CAEmitterLayer下雪❄️动画
5. 动画示例(五) —— QuartzCore之CAEmitterLayer烟花动画

功能要求

QuartzCore框架中CAEmitterLayerCAReplicatorLayerCAGradientLayer是很重要的三个类,特别是在做动画的时候很给力,这里就简单的实现基于它们三个的简单动画,这个动画不是我原创,下面给出链接,致敬原作者。
GitHub - YUAnimation


功能实现

首先看一下CAReplicatorLayer类的API

/* CoreAnimation - CAReplicatorLayer.h

   Copyright (c) 2008-2017, Apple Inc.
   All rights reserved. */

#import 

NS_ASSUME_NONNULL_BEGIN

/* The replicator layer creates a specified number of copies of its
 * sublayers, each copy potentially having geometric, temporal and
 * color transformations applied to it.
 *
 * Note: the CALayer -hitTest: method currently only tests the first
 * instance of z replicator layer's sublayers. This may change in the
 * future. */

CA_CLASS_AVAILABLE (10.6, 3.0, 9.0, 2.0)
@interface CAReplicatorLayer : CALayer

/* The number of copies to create, including the source object.
 * Default value is one (i.e. no extra copies). Animatable. */

@property NSInteger instanceCount;

/* Defines whether this layer flattens its sublayers into its plane or
 * not (i.e. whether it's treated similarly to a transform layer or
 * not). Defaults to NO. If YES, the standard restrictions apply (see
 * CATransformLayer.h). */

@property BOOL preservesDepth;

/* The temporal delay between replicated copies. Defaults to zero.
 * Animatable. */

@property CFTimeInterval instanceDelay;

/* The matrix applied to instance k-1 to produce instance k. The matrix
 * is applied relative to the center of the replicator layer, i.e. the
 * superlayer of each replicated sublayer. Defaults to the identity
 * matrix. Animatable. */

@property CATransform3D instanceTransform;

/* The color to multiply the first object by (the source object). Defaults
 * to opaque white. Animatable. */

@property(nullable) CGColorRef instanceColor;

/* The color components added to the color of instance k-1 to produce
 * the modulation color of instance k. Defaults to the clear color (no
 * change). Animatable. */

@property float instanceRedOffset;
@property float instanceGreenOffset;
@property float instanceBlueOffset;
@property float instanceAlphaOffset;

@end

NS_ASSUME_NONNULL_END

接着再看一下类CAGradientLayer

/* CoreAnimation - CAGradientLayer.h

   Copyright (c) 2008-2017, Apple Inc.
   All rights reserved. */

/* The gradient layer draws a color gradient over its background color,
 * filling the shape of the layer (i.e. including rounded corners). */

#import 
#import 

NS_ASSUME_NONNULL_BEGIN

CA_CLASS_AVAILABLE (10.6, 3.0, 9.0, 2.0)
@interface CAGradientLayer : CALayer

/* The array of CGColorRef objects defining the color of each gradient
 * stop. Defaults to nil. Animatable. */

@property(nullable, copy) NSArray *colors;

/* An optional array of NSNumber objects defining the location of each
 * gradient stop as a value in the range [0,1]. The values must be
 * monotonically increasing. If a nil array is given, the stops are
 * assumed to spread uniformly across the [0,1] range. When rendered,
 * the colors are mapped to the output colorspace before being
 * interpolated. Defaults to nil. Animatable. */

@property(nullable, copy) NSArray *locations;

/* The start and end points of the gradient when drawn into the layer's
 * coordinate space. The start point corresponds to the first gradient
 * stop, the end point to the last gradient stop. Both points are
 * defined in a unit coordinate space that is then mapped to the
 * layer's bounds rectangle when drawn. (I.e. [0,0] is the bottom-left
 * corner of the layer, [1,1] is the top-right corner.) The default values
 * are [.5,0] and [.5,1] respectively. Both are animatable. */

@property CGPoint startPoint;
@property CGPoint endPoint;

/* The kind of gradient that will be drawn. Currently the only allowed
 * value is `axial' (the default value). */

@property(copy) NSString *type;

@end

/** `type' values. **/

CA_EXTERN NSString * const kCAGradientLayerAxial
    CA_AVAILABLE_STARTING (10.6, 3.0, 9.0, 2.0);

NS_ASSUME_NONNULL_END

下面我们就看一下代码

1. YUMainTableViewController.h
#import 

@interface YUMainTableViewController : UITableViewController

@end
2. YUMainTableViewController.m
#import "YUMainTableViewController.h"
#import "YUAnimationViewController.h"

@interface YUMainTableViewController ()
{
    // 各种layer动画
    NSArray *_layerTypes;
}

@end

@implementation YUMainTableViewController

- (void)viewDidLoad {
    [super viewDidLoad];

    self.title = @"三种特殊的layer动画";
    _layerTypes = @[@"CAReplicatorLayer 复制层动画", @"CAEmitterLayer 粒子动画", @"CAGradientLayer 渐变动画"];
    
    self.tableView.tableFooterView = [[UIView alloc] init];
}



#pragma mark - Table view data source

- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section {
    return _layerTypes.count;
}

- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
    
    static NSString *cellID = @"cellID";
    UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:cellID];
    
    if (cell == nil) {
        cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:cellID];
    }
    cell.textLabel.text = _layerTypes[indexPath.row];
    return cell;
}

- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath
{
    YUAnimationViewController *animationCtrl = [[YUAnimationViewController alloc] init];
    animationCtrl.layerType = indexPath.row;
    animationCtrl.title = _layerTypes[indexPath.row];
    [self.navigationController pushViewController:animationCtrl animated:YES];
}

@end
3. YUAnimationViewController.h
#import 
#import "YUReplicatorAnimation.h"

// layer类型
typedef NS_ENUM(NSUInteger, YULayerType){
    YUCAReplicatorLayer,  // 复制动画
    YUCAEmitterLayer,     // 粒子动画
    YUCAGradientLayer,    // 渐变动画
};

@interface YUAnimationViewController : UITableViewController

@property (nonatomic, assign) YULayerType layerType;

@property (nonatomic, assign) YUReplicatorLayerType replicatorLayerType;

@end
4. YUAnimationViewController.m
#import "YUAnimationViewController.h"
#import "YULoadingLabel.h"
#import "SnowView.h"

@interface YUAnimationViewController ()
{
    // 各种CAReplicatorLayer的动画
    NSArray *_animationTypes;
}

@property (nonatomic, strong) SnowView *snow;

@end

@implementation YUAnimationViewController

- (void)viewDidLoad {
    [super viewDidLoad];
    
    self.tableView.tableFooterView = [[UIView alloc] init];
}

// 三种layer的动画
- (void)setLayerType:(YULayerType)layerType
{
    
    switch (layerType) {
        case YUCAReplicatorLayer:
        {
            [self setupCAReplicatorLayerAnimation];
        }
            break;
        case YUCAEmitterLayer:
        {
            [self setupCAEmitterLayer];
            self.view.backgroundColor = [UIColor grayColor];
        }
            break;
        case YUCAGradientLayer:
        {
            [self setupCAGradientLayer];
            self.view.backgroundColor = [UIColor grayColor];
        }
            break;
        default:
            break;
    }
}

#pragma mark -----------------------  各种CAReplicatorLayer的动画

- (void)setReplicatorLayerType:(YUReplicatorLayerType)replicatorLayerType
{
    CGFloat width = 100;
    CGRect viewframe = CGRectMake((kScreenWidth - width) / 2, 150, width, width);
    
    switch (replicatorLayerType) {
            
        case YUReplicatorLayerShake:
            viewframe = CGRectMake(80, 200, width, width);
            break;
            
        case YUReplicatorLayerHeart:
            viewframe = CGRectMake(0, 64, width, width);
            break;
        default:
            break;
    }
    
    UIView *aniView = [[UIView alloc] initWithFrame:viewframe];
    [self.view addSubview:aniView];
    [aniView.layer addSublayer: [YUReplicatorAnimation replicatorLayerWithType:replicatorLayerType]];
    self.view.backgroundColor = [UIColor grayColor];
}

- (void)setupCAReplicatorLayerAnimation
{
    _animationTypes = @[@"波纹 animation", @"波浪 animation", @"三角形 animation", @"网格 animation", @"条形 animation", @"转圈 animation", @"心 animation", @"翻转 animation"];
}

#pragma mark -----------------------  CAEmitterLayer 粒子动画

- (void)setupCAEmitterLayer
{
    self.snow = [[SnowView alloc] initWithFrame:CGRectMake(100, 100, kScreenWidth / 2.f, kScreenWidth / 2.f)];
    //    self.snow.backgroundColor = [UIColor grayColor];
    [self.view addSubview:self.snow];
    self.snow.snowImage  = [UIImage imageNamed:@"snow"];
    self.snow.birthRate  = 20.f;
    self.snow.gravity    = 5.f;
    self.snow.snowColor  = [UIColor whiteColor];
    
    CALayer *layer    = [CALayer layer];
    layer.anchorPoint = CGPointMake(0, 0);                          // 重置锚点
    layer.bounds      = CGRectMake(0, 0, kScreenWidth / 2.f, kScreenWidth / 2.f);  // 设置尺寸
    UIImage *image = [UIImage imageNamed:@"alpha"];
    if (image) {
        layer.contents = (__bridge id)(image.CGImage);
    }
    
    self.snow.layer.mask = layer;
    [self.snow showSnow];
}

#pragma mark -----------------------  渐变动画

- (void)setupCAGradientLayer
{
    // CAGradientLayer 动画
    YULoadingLabel *loadingLabel = [YULoadingLabel loadingLabel];
    loadingLabel.frame = CGRectMake(100, 100, 200, 100);
    [loadingLabel showLoadingInView:self.view text:nil];
    
    YULoadingLabel *loadingLabel2 = [YULoadingLabel loadingLabel];
    loadingLabel.textColor = [UIColor redColor];
    loadingLabel2.frame = CGRectMake(110, 200, 200, 100);
    [loadingLabel2 showLoadingInView:self.view text:@"加载动画"];
}

#pragma mark - Table view data source

- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section {
    return _animationTypes.count;
}

- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
    
    static NSString *cellID = @"cellID";
    UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:cellID];
    
    if (cell == nil) {
        cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:cellID];
    }
    cell.textLabel.text = _animationTypes[indexPath.row];
    return cell;
}

- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath
{
    YUAnimationViewController *animationCtrl = [[YUAnimationViewController alloc] init];
    animationCtrl.replicatorLayerType = indexPath.row;
    animationCtrl.title = _animationTypes[indexPath.row];
    [self.navigationController pushViewController:animationCtrl animated:YES];
}

@end
5. YUReplicatorAnimation.h
#import 

// 动画类型
typedef NS_ENUM(NSUInteger, YUReplicatorLayerType){
    YUReplicatorLayerCircle,
    YUReplicatorLayerWave,
    YUReplicatorLayerTriangle,
    YUReplicatorLayerGrid,
    YUReplicatorLayerShake,
    YUReplicatorLayerRound,
    YUReplicatorLayerHeart,
    YUReplicatorLayerTurn,
};

@interface YUReplicatorAnimation : NSObject

+ (CALayer *)replicatorLayerWithType:(YUReplicatorLayerType)type;

// 波纹
+ (CALayer *)replicatorLayer_Circle;

// 波浪
+ (CALayer *)replicatorLayer_Wave;

// 三角形
+ (CALayer *)replicatorLayer_Triangle;

// 网格
+ (CALayer *)replicatorLayer_Grid;

// 震东条
+ (CALayer *)replicatorLayer_Shake;

// 转圈动画
+ (CALayer *)replicatorLayer_Round;

// 心动画
+ (CALayer *)replicatorLayer_Heart;

@end
6. YUReplicatorAnimation.m
#import "YUReplicatorAnimation.h"

@implementation YUReplicatorAnimation

+ (CALayer *)replicatorLayerWithType:(YUReplicatorLayerType)type
{
    CALayer *layer = nil;
    
    switch (type) {
        case YUReplicatorLayerCircle:
        {
            layer = [self replicatorLayer_Circle];
        }
            break;
        case YUReplicatorLayerWave:
        {
            layer = [self replicatorLayer_Wave];
        }
            break;
        case YUReplicatorLayerTriangle:
        {
            layer = [self replicatorLayer_Triangle];
        }
            break;
        case YUReplicatorLayerGrid:
        {
            layer = [self replicatorLayer_Grid];
        }
            break;
        case YUReplicatorLayerShake:
        {
            layer = [self replicatorLayer_Shake];
        }
            break;
        case YUReplicatorLayerRound:
        {
            layer = [self replicatorLayer_Round];
        }
            break;
        case YUReplicatorLayerHeart:
        {
            layer = [self replicatorLayer_Heart];
        }
            break;
        case YUReplicatorLayerTurn:
        {
            layer = [self replicatorLayer_Turn];
        }
            break;
        default:
        {
            layer = [self replicatorLayer_Circle];
        }
            break;
    }
    
    return layer;
}

#pragma mark -----------------------  复制层

// 圆圈动画 波纹
+ (CALayer *)replicatorLayer_Circle{
    CAShapeLayer *shapeLayer = [CAShapeLayer layer];
    shapeLayer.frame = CGRectMake(0, 0, 80, 80);
    shapeLayer.path = [UIBezierPath bezierPathWithOvalInRect:CGRectMake(0, 0, 80, 80)].CGPath;
    shapeLayer.fillColor = [UIColor redColor].CGColor;
    shapeLayer.opacity = 0.0;
    
    CAAnimationGroup *animationGroup = [CAAnimationGroup animation];
    animationGroup.animations = @[[self alphaAnimation],[self scaleAnimation]];
    animationGroup.duration = 4.0;
    animationGroup.autoreverses = NO;
    animationGroup.repeatCount = HUGE;
    [shapeLayer addAnimation:animationGroup forKey:@"animationGroup"];
    
    CAReplicatorLayer *replicatorLayer = [CAReplicatorLayer layer];
    replicatorLayer.frame = CGRectMake(0, 0, 80, 80);
    replicatorLayer.instanceDelay = 0.5;
    replicatorLayer.instanceCount = 8;
    [replicatorLayer addSublayer:shapeLayer];
    return replicatorLayer;
}

// 波动动画
+ (CALayer *)replicatorLayer_Wave{
    CGFloat between = 5.0;
    CGFloat radius = (100-2*between)/3;
    CAShapeLayer *shapeLayer = [CAShapeLayer layer];
    shapeLayer.frame = CGRectMake(0, (100-radius)/2, radius, radius);
    shapeLayer.path = [UIBezierPath bezierPathWithOvalInRect:CGRectMake(0, 0, radius, radius)].CGPath;
    shapeLayer.fillColor = [UIColor redColor].CGColor;
    [shapeLayer addAnimation:[self scaleAnimation1] forKey:@"scaleAnimation"];
    
    CAReplicatorLayer *replicatorLayer = [CAReplicatorLayer layer];
    replicatorLayer.frame = CGRectMake(0, 0, 100, 100);
    replicatorLayer.instanceDelay = 0.2;
    replicatorLayer.instanceCount = 3;
    replicatorLayer.instanceTransform = CATransform3DMakeTranslation(between*2+radius,0,0);
    [replicatorLayer addSublayer:shapeLayer];
    
    return replicatorLayer;
}

// 三角形动画
+ (CALayer *)replicatorLayer_Triangle{
    CGFloat radius = 100/4;
    CGFloat transX = 100 - radius;
    CAShapeLayer *shapeLayer = [CAShapeLayer layer];
    shapeLayer.frame = CGRectMake(0, 0, radius, radius);
    shapeLayer.path = [UIBezierPath bezierPathWithOvalInRect:CGRectMake(0, 0, radius, radius)].CGPath;
    shapeLayer.strokeColor = [UIColor redColor].CGColor;
    shapeLayer.fillColor = [UIColor redColor].CGColor;
    shapeLayer.lineWidth = 1;
    [shapeLayer addAnimation:[self rotationAnimation:transX] forKey:@"rotateAnimation"];
    
    CAReplicatorLayer *replicatorLayer = [CAReplicatorLayer layer];
    replicatorLayer.frame = CGRectMake(0, 0, radius, radius);
    replicatorLayer.instanceDelay = 0.0;
    replicatorLayer.instanceCount = 3;
    CATransform3D trans3D = CATransform3DIdentity;
    trans3D = CATransform3DTranslate(trans3D, transX, 0, 0);
    trans3D = CATransform3DRotate(trans3D, 120.0*M_PI/180.0, 0.0, 0.0, 1.0);
    replicatorLayer.instanceTransform = trans3D;
    [replicatorLayer addSublayer:shapeLayer];
    
    return replicatorLayer;
}

// 网格动画
+ (CALayer *)replicatorLayer_Grid{
    NSInteger column = 3;
    CGFloat between = 5.0;
    CGFloat radius = (100 - between * (column - 1))/column;
    CAShapeLayer *shapeLayer = [CAShapeLayer layer];
    shapeLayer.frame = CGRectMake(0, 0, radius, radius);
    shapeLayer.path = [UIBezierPath bezierPathWithOvalInRect:CGRectMake(0, 0, radius, radius)].CGPath;
    shapeLayer.fillColor = [UIColor redColor].CGColor;
    
    CAAnimationGroup *animationGroup = [CAAnimationGroup animation];
    animationGroup.animations = @[[self scaleAnimation1], [self alphaAnimation]];
    animationGroup.duration = 1.0;
    animationGroup.autoreverses = YES;
    animationGroup.repeatCount = HUGE;
    [shapeLayer addAnimation:animationGroup forKey:@"groupAnimation"];
    
    CAReplicatorLayer *replicatorLayerX = [CAReplicatorLayer layer];
    replicatorLayerX.frame = CGRectMake(0, 0, 100, 100);
    replicatorLayerX.instanceDelay = 0.3;
    replicatorLayerX.instanceCount = column;
    replicatorLayerX.instanceTransform = CATransform3DTranslate(CATransform3DIdentity, radius+between, 0, 0);
    [replicatorLayerX addSublayer:shapeLayer];
    
    CAReplicatorLayer *replicatorLayerY = [CAReplicatorLayer layer];
    replicatorLayerY.frame = CGRectMake(0, 0, 100, 100);
    replicatorLayerY.instanceDelay = 0.3;
    replicatorLayerY.instanceCount = column;
    replicatorLayerY.instanceTransform = CATransform3DTranslate(CATransform3DIdentity, 0, radius+between, 0);
    [replicatorLayerY addSublayer:replicatorLayerX];
    
    return replicatorLayerY;
}

// 震动条动画
+ (CALayer *)replicatorLayer_Shake{
    
    CALayer *layer = [[CALayer alloc]init];
    layer.frame = CGRectMake(0, 0, 10, 80);
    layer.backgroundColor = [UIColor redColor].CGColor;
    layer.anchorPoint = CGPointMake(0.5, 1);
    // 添加一个基本动画
    [layer addAnimation:[self scaleYAnimation] forKey:@"scaleAnimation"];
    
    CAReplicatorLayer *replicatorLayer = [CAReplicatorLayer layer];
    replicatorLayer.frame = CGRectMake(0, 0, 80, 80);
    
    replicatorLayer.instanceCount = 6;
    replicatorLayer.instanceTransform =  CATransform3DMakeTranslation(45, 0, 0);
    replicatorLayer.instanceDelay = 0.2;
    replicatorLayer.instanceGreenOffset = -0.3;
    [replicatorLayer addSublayer:layer];
    return replicatorLayer;
}

// 转圈动画
+ (CALayer *)replicatorLayer_Round{
    
    CALayer *layer = [[CALayer alloc]init];
    layer.frame = CGRectMake(0, 0, 12, 12);
    layer.cornerRadius = 6;
    layer.masksToBounds = YES;
    layer.transform = CATransform3DMakeScale(0.01, 0.01, 0.01);
    layer.backgroundColor = [UIColor purpleColor].CGColor;
    
    CABasicAnimation *animation = [CABasicAnimation animationWithKeyPath:@"transform.scale"];
    animation.duration = 1;
    animation.repeatCount = MAXFLOAT;
    animation.fromValue = @(1);
    animation.toValue = @(0.01);
    [layer addAnimation:animation forKey:nil];
    
    NSInteger instanceCount = 9;
    
    CAReplicatorLayer *replicatorLayer = [CAReplicatorLayer layer];
    replicatorLayer.frame = CGRectMake(0, 0, 50, 50);
    replicatorLayer.preservesDepth = YES;
    replicatorLayer.instanceColor = [UIColor whiteColor].CGColor;
    replicatorLayer.instanceRedOffset = 0.1;
    replicatorLayer.instanceGreenOffset = 0.1;
    replicatorLayer.instanceBlueOffset = 0.1;
    replicatorLayer.instanceAlphaOffset = 0.1;
    replicatorLayer.instanceCount = instanceCount;
    replicatorLayer.instanceDelay = 1.0/instanceCount;
    replicatorLayer.instanceTransform = CATransform3DMakeRotation((2 * M_PI) /instanceCount, 0, 0, 1);
    [replicatorLayer addSublayer:layer];
    
    return replicatorLayer;
}

// 心动画
+ (CALayer *)replicatorLayer_Heart{
    
    CAReplicatorLayer *replicatorLayer = [CAReplicatorLayer new];
    replicatorLayer.frame = CGRectMake(0, 0, 200, 200);
    //    replicatorLayer.backgroundColor = [UIColor colorWithWhite:0 alpha:0.75].CGColor;
    
    CALayer *subLayer = [CALayer new];
    subLayer.bounds = CGRectMake(60, 105, 10, 10);
    subLayer.backgroundColor = [UIColor colorWithWhite:0.8 alpha:1.0].CGColor;
    subLayer.borderColor = [UIColor colorWithWhite:1.0 alpha:1.0].CGColor;
    subLayer.borderWidth = 1.0;
    subLayer.cornerRadius = 5.0;
    subLayer.shouldRasterize = YES;
    subLayer.rasterizationScale = [UIScreen mainScreen].scale;
    [replicatorLayer addSublayer:subLayer];
    
    CAKeyframeAnimation *move = [CAKeyframeAnimation animationWithKeyPath:@"position"];
    move.path = [self heartPath];
    move.repeatCount = INFINITY;
    move.duration = 6.0;
    //    move.autoreverses = YES;
    [subLayer addAnimation:move forKey:nil];
    
    replicatorLayer.instanceDelay = 6/50.0;
    replicatorLayer.instanceCount = 50;
    replicatorLayer.instanceColor = [UIColor orangeColor].CGColor;
    replicatorLayer.instanceGreenOffset = -0.03;
    
    return replicatorLayer;
}

// 翻转动画
+ (CALayer *)replicatorLayer_Turn
{
    CGFloat margin = 8.0;
    CGFloat width = 80;
    CGFloat dotW = (width - 2 * margin) / 3;
    
    CAShapeLayer *shapeLayer = [CAShapeLayer layer];
    shapeLayer.frame = CGRectMake(0, (width - dotW) * 0.5, dotW, dotW);
    shapeLayer.path = [UIBezierPath bezierPathWithRect:CGRectMake(0, 0, dotW, dotW)].CGPath;
    shapeLayer.fillColor = [UIColor redColor].CGColor;
    
    CAReplicatorLayer *replicatorLayer = [CAReplicatorLayer layer];
    replicatorLayer.frame = CGRectMake(0, 0, width, width);
    replicatorLayer.instanceDelay = 0.1;
    replicatorLayer.instanceCount = 3;
    CATransform3D transform = CATransform3DMakeTranslation(margin + dotW, 0, 0);
    
    replicatorLayer.instanceTransform = transform;
    [replicatorLayer addSublayer:shapeLayer];
    
    CABasicAnimation *basicAnima = [CABasicAnimation animationWithKeyPath:@"transform"];
    basicAnima.fromValue = [NSValue valueWithCATransform3D:CATransform3DRotate(CATransform3DIdentity, 0, 0, 1.0, 0)];
    basicAnima.toValue = [NSValue valueWithCATransform3D:CATransform3DRotate(CATransform3DIdentity, M_PI, 0, 1.0, 0)];
    basicAnima.repeatCount = HUGE;
    basicAnima.duration = 0.6;
    
    [shapeLayer addAnimation:basicAnima forKey:nil];
    
    return replicatorLayer;
}

#pragma mark -----------------------  基础动画

+ (CGPathRef)heartPath
{
    CGFloat W = 25;
    CGFloat marginX = 10;
    CGFloat marginY = 15/25.0 * W;
    CGFloat space = 5/25.0 * W;
    
    UIBezierPath *bezierPath = [UIBezierPath new];
    
    [bezierPath moveToPoint:(CGPointMake(marginX + W * 2, W * 4 + space))];
    [bezierPath addQuadCurveToPoint:CGPointMake(marginX, W * 2) controlPoint:CGPointMake(W, W * 4 - space)];
    [bezierPath addCurveToPoint:CGPointMake(marginX + W * 2, W * 2) controlPoint1:CGPointMake(marginX, marginY) controlPoint2:CGPointMake(marginX + W * 2, marginY)];
    [bezierPath addCurveToPoint:CGPointMake(marginX + W * 4, W * 2) controlPoint1:CGPointMake(marginX + W * 2, marginY) controlPoint2:CGPointMake(marginX + W * 4, marginY)];
    [bezierPath addQuadCurveToPoint:CGPointMake(marginX + W * 2, W * 4 + space) controlPoint:CGPointMake(marginX * 2 + W * 3, W * 4 - space)];
    
    [bezierPath closePath];
    
    CGAffineTransform T = CGAffineTransformMakeScale(3.0, 3.0);
    return CGPathCreateCopyByTransformingPath(bezierPath.CGPath, &T);
}


+ (CABasicAnimation *)scaleYAnimation{
    CABasicAnimation *anim = [CABasicAnimation animationWithKeyPath:@"transform.scale.y"];
    anim.toValue = @0.1;
    anim.duration = 0.4;
    anim.autoreverses = YES;//往返都有动画
    anim.repeatCount = MAXFLOAT;//执行次数
    return anim;
}

+ (CABasicAnimation *)alphaAnimation{
    CABasicAnimation *alpha = [CABasicAnimation animationWithKeyPath:@"opacity"];
    alpha.fromValue = @(1.0);
    alpha.toValue = @(0.0);
    return alpha;
}

+ (CABasicAnimation *)scaleAnimation{
    CABasicAnimation *scale = [CABasicAnimation animationWithKeyPath:@"transform"];
    scale.fromValue = [NSValue valueWithCATransform3D:CATransform3DScale(CATransform3DIdentity, 0.0, 0.0, 0.0)];
    scale.toValue = [NSValue valueWithCATransform3D:CATransform3DScale(CATransform3DIdentity, 1.0, 1.0, 0.0)];
    return scale;
}

+ (CABasicAnimation *)scaleAnimation1{
    CABasicAnimation *scale = [CABasicAnimation animationWithKeyPath:@"transform"];
    scale.fromValue = [NSValue valueWithCATransform3D:CATransform3DScale(CATransform3DIdentity, 1.0, 1.0, 0.0)];
    scale.toValue = [NSValue valueWithCATransform3D:CATransform3DScale(CATransform3DIdentity, 0.2, 0.2, 0.0)];
    scale.autoreverses = YES;
    scale.repeatCount = HUGE;
    scale.duration = 0.6;
    return scale;
}

+ (CABasicAnimation *)rotationAnimation:(CGFloat)transX{
    CABasicAnimation *scale = [CABasicAnimation animationWithKeyPath:@"transform"];
    CATransform3D fromValue = CATransform3DRotate(CATransform3DIdentity, 0.0, 0.0, 0.0, 0.0);
    scale.fromValue = [NSValue valueWithCATransform3D:fromValue];
    
    CATransform3D toValue = CATransform3DTranslate(CATransform3DIdentity, transX, 0.0, 0.0);
    toValue = CATransform3DRotate(toValue,120.0*M_PI/180.0, 0.0, 0.0, 1.0);
    
    scale.toValue = [NSValue valueWithCATransform3D:toValue];
    scale.autoreverses = NO;
    scale.repeatCount = HUGE;
    scale.timingFunction = [CAMediaTimingFunction functionWithName:kCAMediaTimingFunctionEaseInEaseOut];
    scale.duration = 0.8;
    return scale;
}

@end
7. YULoadingLabel.h
#import 

#define kScreenWidth [UIScreen mainScreen].bounds.size.width
#define kScreenHeight [UIScreen mainScreen].bounds.size.height

@interface YULoadingLabel : UILabel

// 创建Label
+ (YULoadingLabel *)loadingLabel;

// 显示加载动画
- (void)showLoadingInView:(UIView *)view text:(NSString *)text;

// 隐藏
- (void)hide;

@end

8. YULoadingLabel.m
#import "YULoadingLabel.h"

@implementation YULoadingLabel

+ (YULoadingLabel *)loadingLabel
{
    return [[self alloc] init];
}

- (instancetype)initWithFrame:(CGRect)frame
{
    self = [super initWithFrame:frame];
    if (self) {
        
        self.textColor = [UIColor blueColor];
        self.font = [UIFont systemFontOfSize:35];
        self.text = @"timelyRain";
    }
    return self;
}


- (void)showLoadingInView:(UIView *)view text:(NSString *)text
{
    [view addSubview:self];
 
    if (text) {
        self.text = text;
    }
    
    
    [self sizeToFit];
    
    // 创建渐变效果的layer
    CAGradientLayer *graLayer = [CAGradientLayer layer];
    graLayer.frame = self.bounds;
    graLayer.colors = @[(__bridge id)[[UIColor greenColor] colorWithAlphaComponent:0.3].CGColor,
                        (__bridge id)[UIColor yellowColor].CGColor,
                        (__bridge id)[[UIColor yellowColor] colorWithAlphaComponent:0.3].CGColor];
    
    graLayer.startPoint = CGPointMake(0, 0.1);//设置渐变方向起点
    graLayer.endPoint = CGPointMake(1, 0);  //设置渐变方向终点
    graLayer.locations = @[@(0.0), @(0.0), @(0.1)]; //colors中各颜色对应的初始渐变点
    
    // 创建动画
    CABasicAnimation *animation = [CABasicAnimation animationWithKeyPath:@"locations"];
    animation.duration = 1.0f;
    animation.toValue = @[@(0.9), @(1.0), @(1.0)];
    animation.removedOnCompletion = NO;
    animation.repeatCount = HUGE_VALF;
    animation.fillMode = kCAFillModeForwards;
    [graLayer addAnimation:animation forKey:@"xindong"];
    
    // 将graLayer设置成textLabel的遮罩
    self.layer.mask = graLayer;
}

- (void)hide
{
    [self removeFromSuperview];
}

@end
9. EmitterLayerView.h
#import 

typedef enum : NSUInteger {
    
    __SNOW = 0x11,
    __RAIN,
    __NONE,
    
} EMitterType;

@interface EmitterLayerView : UIView

/**
 * 重写setter,getter方法,可以在程序中直接使用点语法
 */
- (void)setEmitterLayer:(CAEmitterLayer *)layer;
- (CAEmitterLayer *)emitterLayer;

- (void)show;
- (void)hide;
- (void)configType:(EMitterType)type;

@end
10.EmitterLayerView.m
#import "EmitterLayerView.h"

@interface EmitterLayerView () {
    
    CAEmitterLayer *_layer;
}

@end

@implementation EmitterLayerView

/**
 *  替换layer
 *
 *  @return 替换当前view的layer
 */
+ (Class)layerClass {
    
    return [CAEmitterLayer class];
}

/**
 *  模拟setter,getter方法
 *
 */
- (void)setEmitterLayer:(CAEmitterLayer *)layer {
    
    _layer = layer;
}

- (CAEmitterLayer *)emitterLayer {
    
    return _layer;
}

- (instancetype)initWithFrame:(CGRect)frame {
    
    self = [super initWithFrame:frame];
    
    if (self) {
        
        _layer = (CAEmitterLayer *)self.layer;
    }
    return self;
}

- (void)show {

}

- (void)hide {
    
}

- (void)configType:(EMitterType)type {

}

@end
11. SnowView.h
#import "EmitterLayerView.h"

@interface SnowView : EmitterLayerView

@property (nonatomic, strong) UIImage *snowImage;

@property (nonatomic, assign) CGFloat   lifetime;   // 生命周期
@property (nonatomic, assign) CGFloat   birthRate;  // 出生率
@property (nonatomic, assign) CGFloat   speed;      // 雪花速率
@property (nonatomic, assign) CGFloat   speedRange; // 速率变化范围 [speed - speedRange , speed + speedRange]
@property (nonatomic, assign) CGFloat   gravity;    // 重力
@property (nonatomic, strong) UIColor  *snowColor;  // 雪花颜色

- (void)showSnow;
- (void)show;
- (void)hide;
- (void)configType:(EMitterType)type;

@end
12.SnowView.m
#import "SnowView.h"

@implementation SnowView

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

- (void)setup {
    
    self.emitterLayer.emitterShape    = kCAEmitterLayerLine;                            // 直线粒子发射器
    self.emitterLayer.emitterMode     = kCAEmitterLayerSurface;                         // ?????
    self.emitterLayer.emitterSize     = self.frame.size;                                // 发射区域
    self.emitterLayer.emitterPosition = CGPointMake(self.bounds.size.width / 2.f, - 5); // 发射中心点位置
}

- (void)showSnow {
    
    if (_snowImage == nil) {
        
        return;
    }
    
    // 创建雪花类型的粒子
    CAEmitterCell *snowflake = [CAEmitterCell emitterCell];
    
    // 粒子的名字
    snowflake.name = @"snow";
    
    // 粒子参数的速度乘数因子
    snowflake.birthRate = (_birthRate > 0 ? _birthRate : 1.f);
    
    // 粒子生命周期
    snowflake.lifetime = (_lifetime > 0 ? _lifetime : 60);
    
    // 粒子速度
    snowflake.velocity = (_speed > 0 ? _speed : 10.f);
    
    // 粒子的速度范围
    snowflake.velocityRange = (_speedRange > 0 ? _speedRange : 10.f);
    
    // 粒子y方向的加速度分量(可以理解为重力)
    snowflake.yAcceleration = (_gravity != 0 ? _gravity : 2.f);
    
    // 每个发射的粒子的初始时候随机的角度
    snowflake.emissionRange = 0.5 * M_PI;
    
    // 粒子旋转角度
    snowflake.spinRange = 0.25 * M_PI;
    
    // 获取图片
    snowflake.contents = (id)_snowImage.CGImage;
    
    // 设置雪花形状的粒子的颜色
    snowflake.color = (_snowColor == nil ? [[UIColor whiteColor] CGColor] :_snowColor.CGColor);

    // 尺寸
    snowflake.scale = 0.5f;
    
    // 尺寸变化范围
    snowflake.scaleRange = 0.3f;

    // 添加粒子
    self.emitterLayer.emitterCells = @[snowflake];
}

- (void)configType:(EMitterType)type {
    
    if (type == __SNOW) {
        // 配置
        self.birthRate = 5.f;
        self.snowImage = [UIImage imageNamed:@"snow"];
        self.snowColor = [UIColor blackColor];
        self.lifetime  = 30.f;
        self.alpha     = 0.f;
        
        UIImageView *snowAlpha = [[UIImageView alloc] initWithFrame:self.bounds];
        snowAlpha.image        = [UIImage imageNamed:@"alpha"];
        self.maskView          = snowAlpha;
    }
}
- (void)show {
    
    [self showSnow];
    
    [UIView animateWithDuration:1.75f animations:^{
        
        self.alpha = 0.5f;
    }];
}
- (void)hide {
    
    [UIView animateWithDuration:0.75 animations:^{
        
        self.alpha = 0.f;
        
    } completion:^(BOOL finished) {
        
    }];
}

@end

功能效果

下面我们就看结果效果图。

动画示例(六) —— QuartzCore之CAEmitterLayer、CAReplicatorLayer和CAGradientLayer简单动画_第1张图片
动画示例(六) —— QuartzCore之CAEmitterLayer、CAReplicatorLayer和CAGradientLayer简单动画_第2张图片
动画示例(六) —— QuartzCore之CAEmitterLayer、CAReplicatorLayer和CAGradientLayer简单动画_第3张图片
动画示例(六) —— QuartzCore之CAEmitterLayer、CAReplicatorLayer和CAGradientLayer简单动画_第4张图片

后记

未完,待续~~~

动画示例(六) —— QuartzCore之CAEmitterLayer、CAReplicatorLayer和CAGradientLayer简单动画_第5张图片

你可能感兴趣的:(动画示例(六) —— QuartzCore之CAEmitterLayer、CAReplicatorLayer和CAGradientLayer简单动画)