核心动画(三)

CAEmitterLayer&CAEmitterCell 属性详讲

CAEmitterLayer可实现⾼性能的粒子引擎,被⽤来创建实现粒子动画;⽐如烟雾、⽕、⾬等效果。

CAEmitterLayer常用属性
@property @property(nullable, copy copy) NSArray *emitterCells; // 用来装粒子的数组
@property @property float float birthRate; // 粒子产生系数,默认1.0
@property @property float float lifetime; // 粒子的生命周期系数,默认1.0
@property @property CGPoint emitterPosition; // 决定了粒子发射形状的中心点
@property @property CGFloat emitterZPosition;
@property @property CGSize emitterSize; // 发射源的尺寸大小
@property @property CGFloat emitterDepth;
@property @property(copy copy) NSString *emitterShape; // 发射源的形状
@property @property(copy copy) NSString *emitterMode; // 发射模式
@property @property(copy copy) NSString *renderMode; // 渲染模式
@property @property BOOL preservesDepth;
@property @property float float velocity; // 粒子速度系数,默认1.0
@property @property float float scale; // 粒子的缩放比例系数,默认1.0
@property @property float float spin; // 粒子的自旋转速度系数,默认1.0
@property @property unsigned unsigned int int seed; // 随机数发生器

CAEmitterLayer发射的粒子并不是杂乱无章的,我们可以设置它发射粒子时的位置、几何图形等。通过以下属性去配置

  • emitterPosition:决定发射源的中心点
    1.如设置layer.emitterPosition = CGPointMake(self.view.bounds.size.width * 0.5, -10);表示x轴方向上就是view的中心点
    2.再设置layer.emitterSize = CGSizeMake(40, 0);的话,就表示self.view.bounds.size.width * 0.5左右两边各20
    3.API还提供了一个emitterZPosition,主要是用在三维坐标中表示深度位置
  • emitterSize:决定发射源的大小
  • emitterShape:表示粒子以什么形状发射出来
    1.kCAEmitterLayerPoint点形状,发射源的形状就是一个点,位置就是emitterPosition设置的位置
    2.kCAEmitterLayerLine线形状,发射源的形状是一条线,位置在rect横向位于垂直方向中间那条
    3.kCAEmitterLayerRectangle矩形状,发射源的形状是一个矩形,就是上面生成的那个矩形rect
    4.kCAEmitterLayerCuboid立体矩形形状(3D),发射源是一个立体矩形,需要设置z方向的数据才能生效,否则就同矩形状
    5.kCAEmitterLayerCircle圆形形状,发射源是一个圆形,形状为矩形包裹的那个圆,二维的
    6.kCAEmitterLayerSphere立体圆形(3D),三维的圆形,同样需要设置z方向的数据,不设置同二维一样

比如设置的红包效果,就是用的kCAEmitterLayerLine,并结合了emitterSize来实现从顶部掉下来的效果,而emitterSizeheight其实是被忽略的。

redpacketLayer.emitterPosition = CGPointMake(100, 100);
redpacketLayer.emitterSize = CGSizeMake(20, 0);
redpacketLayer.emitterShape = kCAEmitterLayerLine;
CAEmitterLayer属性图示
  • emitterMode:发射模式,决定了在特定形状上发射的具体形式是什么。其实就是决定发射的区域在发射形状的哪一部分。
    1.kCAEmitterLayerPoints点模式,发射器是以点的形式发射粒子。如果发射形状是圆形,该点就表示圆心。
    2.kCAEmitterLayerOutline轮廓模式,从形状的边界上发射粒子。
    3.kCAEmitterLayerSurface表面模式,从形状的表面上发射粒子。
    4.kCAEmitterLayerVolume是相对于3D形状的球体内立方体内发射。
CAEmitterCell常用属性

CAEmitterLayer就是粒子的工厂,要想实现效果就需要用到CAEmitterCell

@property(nullable, copy) NSString *name; // 粒子名字,默认为nil @property(getter=isEnabled) BOOL enabled;
@property float birthRate; // 粒子的产生率,默认0
@property float lifetime; // 粒子的生命周期,以秒为单位。默认0 
@property float lifetimeRange; // 粒子的生命周期的范围,以秒为单位。默认为0
@property CGFloat emissionLatitude;// 指定纬度,纬度角代表了在x-z轴平面坐标系中与x轴之间的夹角,默认0:
@property CGFloat emissionLongitude; // 指定经度,经度角代表了在x-y轴平面坐标系中与x轴之间的夹角,默认0:
@property CGFloat emissionRange; //发射角度范围,默认0,以锥形分布开的发射角度。角度用弧度制。粒子均匀分布在这个锥形范围内;
@property CGFloat velocity; // 速度和速度范围,两者默认0
@property CGFloat velocityRange;
@property CGFloat xAcceleration; // x,y,z方向上的加速度分量,三者默认都是0
@property CGFloat yAcceleration;
@property CGFloat zAcceleration;
@property CGFloat scale; // 缩放比例, 默认是1
@property CGFloat scaleRange; // 缩放比例范围,默认是0
@property CGFloat scaleSpeed; // 在生命周期内的缩放速度,默认是0 @property CGFloat spin; // 粒子的平均旋转速度,默认是0
@property CGFloat spinRange; // 自旋转角度范围,弧度制,默认是0 @property(nullable) CGColorRef color; // 粒子的颜色,默认白色
@property float redRange; // 粒子颜色red,green,blue,alpha能改变的范围, 默认0
@property float greenRange;
@property float blueRange;
@property float alphaRange;
@property float redSpeed; // 粒子颜色red,green,blue,alpha在生命周期内的改变速度,默认都是0
@property float greenSpeed;
@property float blueSpeed;
@property float alphaSpeed;
@property(nullable, strong) id contents; // 粒子的内容,为CGImageRef 的对象
@property CGRect contentsRect;
@property CGFloat contentsScale;
@property(copy) NSString *minificationFilter;
@property(copy) NSString *magnificationFilter;
@property float minificationFilterBias;
@property(nullable, copy) NSArray *emitterCells; //粒子里面的粒子
@property(nullable, copy) NSDictionary *style;

CAEmitterCell中决定生命状态的属性

  • lifetime、lifetimeRange:粒子在系统上的生命周期,即存活时间,单位是秒。配合lifetimeRage来让粒子生命周期均匀变化,以便可以让粒子的出现和消失显的更加离散。
  • birthRate每秒钟产生的粒子数量,是浮点数。

CAEmitterCell中决定内容的属性

  • contents:CGImageRef的对象。
  • name:粒子的名字

CAEmitterCell中决定颜色状态的属性

  • color: 是粒子的颜色属性,作用是给粒子上色
  • redRange、greenRange、blueRange、alphaRange:对应的color的RGBA的取值范围,范围值为0~1
  • redSpeed、greenSpeed、blueSpeed、alphaSpeed:对应的粒子的RGBA变化速度,取值范围为0~1,表示每秒钟的RGBA的变化率。

CAEmitterCell中决定飞行轨迹的属性:emissionLongitude、emissionLatitude、 emissionRange
CAEmitterCell的子粒子属性:emitterCells

粒子效果案例实现

下面我们使用CAEmitterLayerCAEmitterCell实现粒子效果

#import "ViewController.h"

@interface ViewController ()
@property (nonatomic, strong) CAEmitterLayer * colorBallLayer;
@end

@implementation ViewController

- (void)viewDidLoad {
    [super viewDidLoad];
    self.view.backgroundColor = [UIColor blackColor];
    [self setupEmitter];
}

- (void)setupEmitter{
    UILabel * label = [[UILabel alloc] initWithFrame:CGRectMake(0, 100, self.view.bounds.size.width, 50)];
    [self.view addSubview:label];
    label.textColor = [UIColor whiteColor];
    label.text = @"轻点或拖动来改变发射源位置";
    label.textAlignment = NSTextAlignmentCenter;

    // 粒子:大批量产生颗粒效果,主要有两个类:
    // 发射类 - CAEmitterLayer是发射源,支持多种粒子效果
    // 发射粒子 - CAEmitterCell表示每一种对应的粒子效果
    /*
     CAEmitterLayer
     emitterPosition:粒子发射形状的中心点
     emitterShape:发射源的形状
        kCAEmitterLayerPoint - 点形状
        kCAEmitterLayerLine - 线段形状
        kCAEmitterLayerRectangle - 矩形状
        kCAEmitterLayerCuboid - 立体矩阵块(3D)
        kCAEmitterLayerCircle - 圆形
        kCAEmitterLayerSphere - 立体圆形(3D)
     emitterMode: 发射模式
        kCAEmitterLayerPoints - 点模式
        kCAEmitterLayerOutline - 轮廓模式
        kCAEmitterLayerSurface - 表面模式
        kCAEmitterLayerVolume - 3D发射模式
     */

    // 1. 设置CAEmitterLayer,创建发射源
    CAEmitterLayer * colorBallLayer = [CAEmitterLayer layer];
    [self.view.layer addSublayer:colorBallLayer];
    self.colorBallLayer = colorBallLayer;
    
    //发射源的尺寸大小
    colorBallLayer.emitterSize = self.view.frame.size;
    //发射源的形状
    colorBallLayer.emitterShape = kCAEmitterLayerPoint;
    //发射模式
    colorBallLayer.emitterMode = kCAEmitterLayerPoints;
    //粒子发射形状的中心点
    colorBallLayer.emitterPosition = CGPointMake(self.view.layer.bounds.size.width, 0.f);
    
    // 2. 配置CAEmitterCell
    CAEmitterCell * colorBallCell = [CAEmitterCell emitterCell];
    //粒子名称
    colorBallCell.name = @"colorBallCell";
    //粒子产生率,默认为0
    colorBallCell.birthRate = 20.f;
    //粒子生命周期
    colorBallCell.lifetime = 10.f;
    //粒子速度,默认为0
    colorBallCell.velocity = 40.f;
    //粒子速度平均量
    colorBallCell.velocityRange = 100.f;
    //x,y,z方向上的加速度分量,三者默认都是0
    colorBallCell.yAcceleration = 15.f;
    //指定纬度,纬度角代表了在x-z轴平面坐标系中与x轴之间的夹角,默认0:
    colorBallCell.emissionLongitude = M_PI; // 向左
    //发射角度范围,默认0,以锥形分布开的发射角度。角度用弧度制。粒子均匀分布在这个锥形范围内;
    colorBallCell.emissionRange = M_PI_4; // 围绕X轴向左90度
    // 缩放比例, 默认是1
    colorBallCell.scale = 0.2;
    // 缩放比例范围,默认是0
    colorBallCell.scaleRange = 0.1;
    // 在生命周期内的缩放速度,默认是0
    colorBallCell.scaleSpeed = 0.02;
    // 粒子的内容,为CGImageRef的对象  
    colorBallCell.contents = (id)[[UIImage imageNamed:@"circle_white"] CGImage];
    //颜色
    colorBallCell.color = [[UIColor colorWithRed:0.5 green:0.f blue:0.5 alpha:1.f] CGColor];
    // 粒子颜色red,green,blue,alpha能改变的范围,默认0
    colorBallCell.redRange = 1.f;
    colorBallCell.greenRange = 1.f;
    colorBallCell.alphaRange = 0.8;
    // 粒子颜色red,green,blue,alpha在生命周期内的改变速度,默认都是0
    colorBallCell.blueSpeed = 1.f;
    // 透明度从1.0慢慢变成0.8,是减小的,所以要用负值
    colorBallCell.alphaSpeed = -0.1f;
    
    // 添加
    colorBallLayer.emitterCells = @[colorBallCell];
}

- (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event{
    CGPoint point = [self locationFromTouchEvent:event];
    [self setBallInPsition:point];
}

- (void)touchesMoved:(NSSet *)touches withEvent:(UIEvent *)event{
    CGPoint point = [self locationFromTouchEvent:event];
    [self setBallInPsition:point];
}

/**
 * 获取手指所在点
 */
- (CGPoint)locationFromTouchEvent:(UIEvent *)event{
    UITouch * touch = [[event allTouches] anyObject];
    return [touch locationInView:self.view];
}

/**
 * 移动发射源到某个点上
 */
- (void)setBallInPsition:(CGPoint)position{
    //创建基础动画
    CABasicAnimation * anim = [CABasicAnimation animationWithKeyPath:@"emitterCells.colorBallCell.scale"];
    //fromValue
    anim.fromValue = @0.2f;
    //toValue
    anim.toValue = @0.5f;
    //duration
    anim.duration = 1.f;
    //线性起搏,使动画在其持续时间内均匀地发生
    anim.timingFunction = [CAMediaTimingFunction functionWithName:kCAMediaTimingFunctionLinear];
    
    // 用事务包装隐式动画,这种动画是通过runloop执行的
    [CATransaction begin];
    //设置是否禁止由于该事务组内的属性更改而触发的操作。
    [CATransaction setDisableActions:YES];
    //为colorBallLayer 添加动画
    [self.colorBallLayer addAnimation:anim forKey:nil];
    //为colorBallLayer 指定位置添加动画效果
    [self.colorBallLayer setValue:[NSValue valueWithCGPoint:position] forKeyPath:@"emitterPosition"];
    //提交动画
    [CATransaction commit];
}

@end
粒子效果

注意:重新切换粒子发射源的位置时,之前的粒子要等到它的生命周期结束才会消失

粒子效果实现下雨场景

将下雨的背景图雨点图素材拖入工程中,添加下雨场景代码如下

#import "ViewController.h"

@interface ViewController ()
@property (nonatomic, strong) CAEmitterLayer * rainLayer;
@property (nonatomic, weak) UIImageView * imageView;
@end

@implementation ViewController

- (void)viewDidLoad {
    [super viewDidLoad];
    [self setupUI];
    [self setupEmitter];
}

- (void)setupUI{
    // 背景图片
    UIImageView * imageView = [[UIImageView alloc]initWithFrame:self.view.frame];
    [self.view addSubview:imageView];
    self.imageView = imageView;
    imageView.image = [UIImage imageNamed:@"rain"];
    
    // 下雨按钮
    UIButton * startBtn = [UIButton buttonWithType:UIButtonTypeCustom];
    [self.view addSubview:startBtn];
    startBtn.frame = CGRectMake(20, self.view.bounds.size.height - 60, 80, 40);
    startBtn.backgroundColor = [UIColor whiteColor];
    [startBtn setTitle:@"雨停了" forState:UIControlStateNormal];
    [startBtn setTitle:@"下雨" forState:UIControlStateSelected];
    [startBtn setTitleColor:[UIColor grayColor] forState:UIControlStateNormal];
    [startBtn setTitleColor:[UIColor redColor] forState:UIControlStateSelected];
    [startBtn addTarget:self action:@selector(buttonClick:) forControlEvents:UIControlEventTouchUpInside];
    
    // 雨量按钮
    UIButton * rainBIgBtn = [UIButton buttonWithType:UIButtonTypeCustom];
    [self.view addSubview:rainBIgBtn];
    rainBIgBtn.tag = 100;
    rainBIgBtn.frame = CGRectMake(140, self.view.bounds.size.height - 60, 80, 40);
    rainBIgBtn.backgroundColor = [UIColor whiteColor];
    [rainBIgBtn setTitle:@"下大点" forState:UIControlStateNormal];
    [rainBIgBtn setTitleColor:[UIColor grayColor] forState:UIControlStateNormal];
    [rainBIgBtn addTarget:self action:@selector(rainButtonClick:) forControlEvents:UIControlEventTouchUpInside];
    
    UIButton * rainSmallBtn = [UIButton buttonWithType:UIButtonTypeCustom];
    [self.view addSubview:rainSmallBtn];
    rainSmallBtn.tag = 200;
    rainSmallBtn.frame = CGRectMake(240, self.view.bounds.size.height - 60, 80, 40);
    rainSmallBtn.backgroundColor = [UIColor whiteColor];
    [rainSmallBtn setTitle:@"太大了" forState:UIControlStateNormal];
    [rainSmallBtn setTitleColor:[UIColor grayColor] forState:UIControlStateNormal];
    [rainSmallBtn addTarget:self action:@selector(rainButtonClick:) forControlEvents:UIControlEventTouchUpInside];
}

- (void)buttonClick:(UIButton *)sender{
    if (!sender.selected) {
        sender.selected = !sender.selected;
        NSLog(@"雨停了");
        // 停止下雨
        // 1. 移除layer,这种方法不美观
        // 2. 修改birthRate的值,推荐使用这种方案
        
        // 停止下雨
        [self.rainLayer setValue:@0.f forKeyPath:@"birthRate"];
    }else{
        sender.selected = !sender.selected;
        NSLog(@"开始下雨了");
        // 开始下雨
        [self.rainLayer setValue:@1.f forKeyPath:@"birthRate"];
    }
}

- (void)rainButtonClick:(UIButton *)sender{
    NSInteger rate = 1;
    CGFloat scale = 0.05;
    if (sender.tag == 100) {
        NSLog(@"下大了");
        
        if (self.rainLayer.birthRate < 30) {
            [self.rainLayer setValue:@(self.rainLayer.birthRate + rate) forKeyPath:@"birthRate"];
            [self.rainLayer setValue:@(self.rainLayer.scale + scale) forKeyPath:@"scale"];
        }
    }else if (sender.tag == 200){
        NSLog(@"变小了");
        
        if (self.rainLayer.birthRate > 1) {
            [self.rainLayer setValue:@(self.rainLayer.birthRate - rate) forKeyPath:@"birthRate"];
            [self.rainLayer setValue:@(self.rainLayer.scale - scale) forKeyPath:@"scale"];
        }
    }
}

- (void)setupEmitter{
    // 1. 设置CAEmitterLayer
    CAEmitterLayer * rainLayer = [CAEmitterLayer layer];
    // 2.在背景图上添加粒子图层
    [self.imageView.layer addSublayer:rainLayer];
    self.rainLayer = rainLayer;
    
    //3.发射形状--线性
    rainLayer.emitterShape = kCAEmitterLayerLine;
    //发射模式
    rainLayer.emitterMode = kCAEmitterLayerSurface;
    //发射源大小
    rainLayer.emitterSize = self.view.frame.size;
    //发射源位置 y最好不要设置为0 最好<0
    rainLayer.emitterPosition = CGPointMake(self.view.bounds.size.width * 0.5, -10);
    
    // 2. 配置cell
    CAEmitterCell * snowCell = [CAEmitterCell emitterCell];
    //粒子内容
    snowCell.contents = (id)[[UIImage imageNamed:@"rain_white"] CGImage];
    //每秒产生的粒子数量的系数
    snowCell.birthRate = 25.f;
    //粒子的生命周期
    snowCell.lifetime = 20.f;
    //speed粒子速度.图层的速率。用于将父时间缩放为本地时间,例如,如果速率是2,则本地时间的进度是父时间的两倍。默认值为1。
    snowCell.speed = 10.f;
    //粒子速度系数, 默认1.0
    snowCell.velocity = 10.f;
    //每个发射物体的初始平均范围,默认等于0
    snowCell.velocityRange = 10.f;
    //粒子在y方向的加速的
    snowCell.yAcceleration = 1000.f;
    //粒子缩放比例: scale
    snowCell.scale = 0.1;
    //粒子缩放比例范围:scaleRange
    snowCell.scaleRange = 0.f;
    
    // 4 .添加到图层上
    rainLayer.emitterCells = @[snowCell];
}

@end
下雨效果

粒子效果实现QQ点赞按钮

动画分析
  • 点赞先把按钮放大 -> 缩小 CAKeyFrameAnimation
  • 放大之后,一个圈的粒子效果: CAEmitterLayer
实现思路
  • 自定义按钮(重写高亮方法)选中/默认状态
  • 配置CAEmitterLayerCAEmitterCell

自定义点赞按钮CCLikeButton


#import 
@interface CCLikeButton : UIButton
@end


#import "CCLikeButton.h"

@interface CCLikeButton()
@property(nonatomic,strong)CAEmitterLayer *explosionLayer;
@end

@implementation CCLikeButton
- (void)awakeFromNib{
    [super awakeFromNib];
    //设置粒子效果
    [self setupExplosion];
}

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

//设置粒子
- (void)setupExplosion{
    // 1. 粒子
    CAEmitterCell * explosionCell = [CAEmitterCell emitterCell];
    explosionCell.name = @"explosionCell";
    //透明值变化速度
    explosionCell.alphaSpeed = -1.f;
    //alphaRange透明值范围
    explosionCell.alphaRange = 0.10;
    //生命周期
    explosionCell.lifetime = 1;
    //生命周期range
    explosionCell.lifetimeRange = 0.1;
    //粒子速度
    explosionCell.velocity = 40.f;
    //粒子速度范围
    explosionCell.velocityRange = 10.f;
    //缩放比例
    explosionCell.scale = 0.08;
    //缩放比例range
    explosionCell.scaleRange = 0.02;
    //粒子图片
    explosionCell.contents = (id)[[UIImage imageNamed:@"spark_red"] CGImage];
    
    // 2.发射源
    CAEmitterLayer * explosionLayer = [CAEmitterLayer layer];
    [self.layer addSublayer:explosionLayer];
    self.explosionLayer = explosionLayer;
    //发射院尺寸大小
    self.explosionLayer.emitterSize = CGSizeMake(self.bounds.size.width + 40, self.bounds.size.height + 40);
    //emitterShape表示粒子从什么形状发射出来,圆形形状
    explosionLayer.emitterShape = kCAEmitterLayerCircle;
    //emitterMode发射模型,轮廓模式,从形状的边界上发射粒子
    explosionLayer.emitterMode = kCAEmitterLayerOutline;
    //renderMode:渲染模式
    explosionLayer.renderMode = kCAEmitterLayerOldestFirst;
    //粒子cell 数组
    explosionLayer.emitterCells = @[explosionCell];
}

-(void)layoutSubviews{
    // 发射源位置
    self.explosionLayer.position = CGPointMake(self.bounds.size.width * 0.5, self.bounds.size.height * 0.5);
    [super layoutSubviews];
}

/**
 * 选中状态 实现缩放
 */
- (void)setSelected:(BOOL)selected{
    [super setSelected:selected];
    // 通过关键帧动画实现缩放
    CAKeyframeAnimation * animation = [CAKeyframeAnimation animation];
    // 设置动画路径
    animation.keyPath = @"transform.scale";
    
    if (selected) {
        // 从没有点击到点击状态 会有爆炸的动画效果,先放大1.5倍 再放大2倍 再缩小到0.8倍 最后还原
        animation.values = @[@1.5,@2.0, @0.8, @1.0];
        animation.duration = 0.5;
        //计算关键帧方式:
        animation.calculationMode = kCAAnimationCubic;
        //为图层添加动画
        [self.layer addAnimation:animation forKey:nil];
        
        // 让放大动画先执行完毕 再执行爆炸动画
        [self performSelector:@selector(startAnimation) withObject:nil afterDelay:0.25];
    }else{
        // 从点击状态normal状态 无动画效果 如果点赞之后马上取消 那么也立马停止动画
        [self stopAnimation];
    }
}

// 没有高亮状态
- (void)setHighlighted:(BOOL)highlighted{
    [super setHighlighted:highlighted];
}

/**
 * 开始动画
 */
- (void)startAnimation{
    // 用KVC设置颗粒个数
    [self.explosionLayer setValue:@1000 forKeyPath:@"emitterCells.explosionCell.birthRate"];
    
    // 开始动画
    self.explosionLayer.beginTime = CACurrentMediaTime();
    
    // 延迟停止动画
    [self performSelector:@selector(stopAnimation) withObject:nil afterDelay:0.15];
}

/**
 * 动画结束
 */
- (void)stopAnimation{
    // 用KVC设置颗粒个数
    [self.explosionLayer setValue:@0 forKeyPath:@"emitterCells.explosionCell.birthRate"];
    //移除动画
    [self.explosionLayer removeAllAnimations];
}

@end

点赞按钮我们已经封装好了,下面我们来使用一下看看效果


#import "ViewController.h"
#import "CCLikeButton.h"

@interface ViewController ()
@end
@implementation ViewController

- (void)viewDidLoad {
    [super viewDidLoad];
    // Do any additional setup after loading the view, typically from a nib.
    self.view.backgroundColor = [UIColor whiteColor];
    
    //添加点赞按钮
    CCLikeButton * btn = [CCLikeButton buttonWithType:UIButtonTypeCustom];
    btn.frame = CGRectMake(200, 150, 30, 130);
    [self.view addSubview:btn];
    [btn setImage:[UIImage imageNamed:@"dislike"] forState:UIControlStateNormal];
    [btn setImage:[UIImage imageNamed:@"like_orange"] forState:UIControlStateSelected];
    [btn addTarget:self action:@selector(btnClick:) forControlEvents:UIControlEventTouchUpInside];
}

- (void)btnClick:(UIButton *)button{
    if (!button.selected) { // 点赞
        button.selected = !button.selected;
        NSLog(@"点赞");
    }else{ // 取消点赞
        button.selected = !button.selected;
        NSLog(@"取消赞");
    }
}

@end
QQ点赞效果

多个图层创建3D叠加

下面实现多个图层叠加的3D旋转效果,代码如下

#import "ViewController.h"
#define DEGREES_TO_RADIANS(d) (d * M_PI / 180)

@interface ViewController ()
@property (nonatomic, strong) CALayer *rootLayer;
@end

@implementation ViewController

- (void)viewDidLoad {
    [super viewDidLoad];
    self.rootLayer = [CALayer layer];
    // 应用透视转换
    CATransform3D transform = CATransform3DMakePerspective(1000);
    self.rootLayer.sublayerTransform = transform;
    self.rootLayer.frame = self.view.bounds;
    [self.view.layer addSublayer:self.rootLayer];
    
    //颜色数组
    NSArray *colors = @[[UIColor colorWithRed:0.263 green:0.769 blue:0.319 alpha:1.000], [UIColor colorWithRed:0.990 green:0.759 blue:0.145 alpha:1.000], [UIColor colorWithRed:0.084 green:0.398 blue:0.979 alpha:1.000]];
    
    //添加3个图层
    [self addLayersWithColors:colors];
    
    [self performSelector:@selector(rotateLayers) withObject:nil afterDelay:1.0];
}

- (void)addLayersWithColors:(NSArray *)colors {
    //遍历颜色数组,创建图层
    for (UIColor *color in colors) {
        //创建图层
        CALayer *layer = [CALayer layer];
        //颜色
        layer.backgroundColor = color.CGColor;
        //大小
        layer.bounds = CGRectMake(0, 0, 200, 200);
        //位置
        layer.position = CGPointMake(160, 190);
        //透明度
        layer.opacity = 0.80;
        //圆角
        layer.cornerRadius = 10;
        //边框颜色
        layer.borderColor = [UIColor whiteColor].CGColor;
        //边框宽度
        layer.borderWidth = 1.0;
        //阴影offset ,默认(0,3)
        layer.shadowOffset = CGSizeMake(0, 2);
        //用于创建阴影的模糊半径。默认值为3。可动画的
        layer.shadowOpacity = 0.35;
        //阴影颜色
        layer.shadowColor = [UIColor darkGrayColor].CGColor;
        //是否光栅化
        layer.shouldRasterize = YES;
        //添加图层
        [self.rootLayer addSublayer:layer];
    }
}

- (void)rotateLayers {
    //创建基本动画以围绕Y轴和Z轴旋转
    CABasicAnimation *transformAnimation = [CABasicAnimation animationWithKeyPath:@"transform"];
    transformAnimation.fromValue = [NSValue valueWithCATransform3D:CATransform3DIdentity];
    transformAnimation.toValue = [NSValue valueWithCATransform3D:CATransform3DMakeRotation(DEGREES_TO_RADIANS(85), 0, 1, 1)];
    transformAnimation.duration = 1.5;
    //自动翻转
    transformAnimation.autoreverses = YES;
    //重复次数
    transformAnimation.repeatCount = HUGE_VALF;
    
    //定义动画步调的计时函数
    transformAnimation.timingFunction = [CAMediaTimingFunction functionWithName:kCAMediaTimingFunctionEaseInEaseOut];
    
    int tx = 0;
    // 循环浏览子图层并附加动画
    for (CALayer *layer in [self.rootLayer sublayers]) {
        //为图层添加动画
        [layer addAnimation:transformAnimation forKey:nil];
        
        // 创建要沿X轴平移的动画
        CABasicAnimation *translateAnimation = [CABasicAnimation animationWithKeyPath:@"transform.translation.x"];
        translateAnimation.fromValue = [NSValue valueWithCATransform3D:layer.transform];
        translateAnimation.toValue = [NSNumber numberWithFloat:tx];
        translateAnimation.duration = 1.5;
        translateAnimation.autoreverses = YES;
        translateAnimation.timingFunction = [CAMediaTimingFunction functionWithName:kCAMediaTimingFunctionEaseInEaseOut];
        translateAnimation.repeatCount = HUGE_VALF;
        [layer addAnimation:translateAnimation forKey:nil];
        tx += 35;
    }
}

//透视投影修改m34值
static CATransform3D CATransform3DMakePerspective(CGFloat z) {
    CATransform3D t = CATransform3DIdentity;
    t.m34 = - 1.0 / z;
    return t;
}

@end
多个图层旋转效果

AVPlayerLayer播放视频

AVPlayerLayer可实现播放视频图层,是AVFoundation提供的图层,但与CoreAnimation紧密链接,虽然不是核心动画中的图层,但AVPlayerLayer的父类是CALayer,就实现核心动画的一些效果。

下面使用AVPlayerLayer来实现视频的播放


#import 
@class AVPlayer;
@interface ViewController : UIViewController
@property (nonatomic, strong) AVPlayer *player;
@end


#import "ViewController.h"
#import 
#define DEGREES_TO_RADIANS(d) (d * M_PI / 180)
@interface ViewController ()

@end

@implementation ViewController

- (void)viewDidLoad {
    [super viewDidLoad];
    //设置背景颜色
    self.view.backgroundColor = [UIColor colorWithRed:0.972 green:0.134 blue:0.173 alpha:1.000];
    self.view.tintColor = [UIColor whiteColor];
    
    // Setup AVPlayer
    //1.视频地址
    NSString *moviePath = [[NSBundle mainBundle] pathForResource:@"cyborg" ofType:@"m4v"];
    //2.创建player对象
    self.player = [AVPlayer playerWithURL:[NSURL fileURLWithPath:moviePath]];
    //3.播放
    [self.player play];
    
    //4.创建和配置AvPlayerlayer
    AVPlayerLayer *playerLayer = [AVPlayerLayer playerLayerWithPlayer:self.player];
    playerLayer.bounds = CGRectMake(0, 0, 300, 170);
    playerLayer.position = CGPointMake(210, 200);
    playerLayer.borderColor = [UIColor whiteColor].CGColor;
    playerLayer.borderWidth = 3.0;
    playerLayer.shadowOffset = CGSizeMake(0, 3);
    playerLayer.shadowOpacity = 0.80;
    
    //5.添加透视投影,可让播放视频的图层旋转
    self.view.layer.sublayerTransform = CATransform3DMakePerspective(1000);
    [self.view.layer addSublayer:playerLayer];
    
    // Set up slider used to rotate video around Y-axis
    UISlider *slider = [[UISlider alloc] initWithFrame:CGRectMake(60, 300, 300, 23)];
    slider.minimumValue = -1.0;
    slider.maximumValue = 1.0;
    slider.continuous = NO;
    slider.value = 0.0;
    [slider addTarget:self action:@selector(sliderValueChanged:) forControlEvents:UIControlEventTouchDragInside];
    [self.view addSubview:slider];
    
    // Spin button to spin the video around the X-axis
    UIButton *spinButton = [UIButton buttonWithType:UIButtonTypeRoundedRect];
    spinButton.frame = CGRectMake(0, 0, 50, 31);
    spinButton.center = CGPointMake(210, 350);
    [spinButton setTitle:@"Spin" forState:UIControlStateNormal];
    [spinButton addTarget:self action:@selector(spinIt) forControlEvents:UIControlEventTouchUpInside];
    [self.view addSubview:spinButton];
}

// Animate spinning video around X-axis,视频图层围绕x轴旋转
- (void)spinIt {
    CALayer *layer = [self.view.layer sublayers][0];
    CABasicAnimation *animation = [CABasicAnimation animationWithKeyPath:@"transform.rotation.x"];
    animation.duration = 1.25f;
    animation.toValue = @(DEGREES_TO_RADIANS(360));
    [layer addAnimation:animation forKey:@"spinAnimation"];
}

// Rotate the layer around the Y-axis as slider value is changed
-(void)sliderValueChanged:(UISlider *)sender{
    CALayer *layer = [self.view.layer sublayers][0];
    layer.transform = CATransform3DMakeRotation([sender value], 0, 1, 0);
}

static CATransform3D CATransform3DMakePerspective(CGFloat z) {
    CATransform3D t = CATransform3DIdentity;
    t.m34 = - 1.0 / z;
    return t;
}

- (void)dealloc {
    [self.player pause];
}

@end
视频播放

你可能感兴趣的:(核心动画(三))