相关文献:
iOS CoreAnimation(一) - 基础知识
iOS CoreAnimation(二) - CALayer/UIBezierPath
iOS CoreAnimation(三) - CAEmitterLayer粒子图层
本文主要内容:
1.了解CAEmitterLayer 类常⽤属性与⽅法
2.了解CAEmitterCell 类常⽤属性与⽅法
3.案例:实现发射粒子效果
4.案例:实现下雨场景
5.案例:实现QQ点赞效果
一、CAEmitterLayer常用属性与方法
CAEmitterLayer
的实现⾼性能的粒⼦引擎,被⽤来创建实现粒⼦动画,⽐如烟雾,⽕,⾬等效果。
API_AVAILABLE(macos(10.6), ios(5.0), watchos(2.0), tvos(9.0))
@interface CAEmitterLayer : CALayer
// emitterCells 存放粒子类型
// 一个发射器可以存放多种的粒子类型,圆形/方形/星形...只需要告诉发射器即可
@property(nullable, copy) NSArray<CAEmitterCell *> *emitterCells;
// 产生粒子速率(默认1.0)
@property float birthRate;
// 粒子的生命周期(默认1.0)
@property float lifetime;
// 粒子发射源的位置,默认(x:0, y:0, z:0)
@property CGPoint emitterPosition;
@property CGFloat emitterZPosition;
//发射源尺寸CAEmitterLayer实例的size/depth, 默认(width:0, height:0, depth:0)
@property CGSize emitterSize;
@property CGFloat emitterDepth;
// 发射源的形状:
/** 值:点形`point' (the default),
线形`line', 矩形`rectangle',
圆形`circle',
3D立体矩形`cuboid',
3D立体圆形`sphere'.
*/
@property(copy) CAEmitterLayerEmitterShape emitterShape;
// 发射源模式:
/** 值:`points'点模式,
`outline'轮廓模式(发射点边界发射出来),
`surface'表面模式
`volume' 3D发射模式(the default).
*/
@property(copy) CAEmitterLayerEmitterMode emitterMode;
// 发射源渲染模式:
// 值:`unordered' (the default), `oldestFirst', `oldestLast', `backToFront' (i.e. sorted into Z order) and `additive'.
@property(copy) CAEmitterLayerRenderMode renderMode;
// 当为true时,粒子被渲染成好像它们直接居住在层的超层的三维坐标空间,而不是首先被平展到层的平面(默认为NO)。
// 如果为true,则“filters”、“backgroundFilters”和阴影相关属性的效果是未定义的。
@property BOOL preservesDepth;
// 乘以粒子定义的粒子速度(默认为1.0)。
@property float velocity;
// 乘以粒子定义的粒子缩放(默认为1.0)。
@property float scale;
// 乘以粒子定义的粒子自旋(默认为1.0)。
@property float spin;
// 用于初始化随机数生成器的种子(默认为0)。
// 每一层都有自己的RNG状态。对于均值为M,范围为R的性质,性质的随机值均匀分布在区间[M - R/2, M + R/2]内。
@property unsigned int seed;
@end
二、CAEmitterCell常⽤属性与⽅法
CAEmitterCell
定义的粒子样式。
API_AVAILABLE(macos(10.6), ios(5.0), watchos(2.0), tvos(9.0))
@interface CAEmitterCell : NSObject <NSSecureCoding, CAMediaTiming>
{
@private
void *_attr[2];
void *_state;
uint32_t _flags;
}
// 创建实例
+ (instancetype)emitterCell;
// 粒子实现与CALayer定义的相同的属性模型。
+ (nullable id)defaultValueForKey:(NSString *)key;
- (BOOL)shouldArchiveValueForKey:(NSString *)key;
// 粒子名称,默认为nil。可以构造多种粒子样式,通过name去区分。
@property(nullable, copy) NSString *name;
// 是否可用,控制是否呈现此发射器中的粒子。
@property(getter=isEnabled) BOOL enabled;
// 每秒创建的发射的粒子数量。默认值0.0
// 产生率
@property float birthRate;
// 每个发射的粒子生命周期(以秒为单位),指定为平均值和平均值的范围。这两个值默认0.0
@property float lifetime; // 生命周期
@property float lifetimeRange; // 生命周期的范围
// 发射粒子的纬度(弧度为单位),X轴与Z轴之间的夹角,默认0.0
@property CGFloat emissionLatitude;
// 发射粒子的经度(弧度为单位),X轴与Y轴之间的夹角,默认0.0
@property CGFloat emissionLongitude;
// 发射角度(弧度为单位),默认0.0。发射的物体均匀地分布在这个锥体上。
@property CGFloat emissionRange;
// 每个发射粒子平均速度,默认为0.0
@property CGFloat velocity;
// 每个发射粒子速度范围,默认为0.0
@property CGFloat velocityRange;
// 应用于发射粒子在对应轴上的加速度矢量。默认(0, 0, 0)
@property CGFloat xAcceleration;
@property CGFloat yAcceleration;
@property CGFloat zAcceleration;
/* The scale factor applied to each emitted object, defined as mean and
* range about the mean. Scale defaults to one, range to zero.
* Animatable. */
// 初始缩放比例,默认1.0,比如一开始就放大2倍就写2
@property CGFloat scale;
// 缩放范围,默认0.0
@property CGFloat scaleRange;
// 缩放速度
@property CGFloat scaleSpeed;
// 应用于每个粒子的旋转速度,定义为平均值和平均值的范围。默认为0。
@property CGFloat spin;
@property CGFloat spinRange;
/* The mean color of each emitted object, and the range from that mean
* color. `color' defaults to opaque white, `colorRange' to (0, 0, 0,
* 0). Animatable. */
// 粒子的颜色,默认白色
@property(nullable) CGColorRef color;
// 以及粒子的颜色变化范围,colorRange为(r:0, g:0, b:0, a:0)。
@property float redRange;
@property float greenRange;
@property float blueRange;
@property float alphaRange;
// 颜色变化速度,默认 (0, 0, 0, 0)
@property float redSpeed;
@property float greenSpeed;
@property float blueSpeed;
@property float alphaSpeed;
// 粒子内容,默认nil。通常是CGImageRef。
@property(nullable, strong) id contents;
// contents的大小,默认为[0 0 1 1]
@property CGRect contentsRect;
// contents的缩放系数
@property CGFloat contentsScale;
// 渲染“contents”图像时使用的过滤器参数。
@property(copy) NSString *minificationFilter;
@property(copy) NSString *magnificationFilter;
@property float minificationFilterBias;
// 粒子里面再套粒子,类似于tableViewCell里也能有tableView的Cell
@property(nullable, copy) NSArray<CAEmitterCell *> *emitterCells;
// 继承属性类似于layers。
@property(nullable, copy) NSDictionary *style;
@end
三、案例:实现发射粒子效果
#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;
//1.创建发射源,类似于UITableView
CAEmitterLayer *colorBallLayer = [CAEmitterLayer layer];
[self.view.layer addSublayer:colorBallLayer];
self.colorBallLayer = colorBallLayer;
//发射源尺寸colorBallLayer的frame
colorBallLayer.emitterSize = self.view.frame.size;
//发射源形状
colorBallLayer.emitterShape = kCAEmitterLayerPoint;
//发射模式
colorBallLayer.emitterMode = kCAEmitterLayerPoints;
//粒子发射的中心点.
colorBallLayer.emitterPosition = CGPointMake(self.view.layer.bounds.size.width, 0);
//2.设置Cell(粒子的样式),类似于UITableViewCell
CAEmitterCell *colorBallCell = [CAEmitterCell emitterCell];
//粒子名称
colorBallCell.name = @"ccCell";
//粒子的产生速率
colorBallCell.birthRate = 20.0f;
//粒子的生命周期
colorBallCell.lifetime = 10.0f;
//粒子速度
colorBallCell.velocity = 40.0f;
colorBallCell.velocityRange = 100.0;
colorBallCell.yAcceleration = 15.0f; // 粒子在Y轴上的加速矢量
// 发射粒子的纬度(弧度为单位),X轴与Z轴之间的夹角,默认0.0
colorBallCell.emissionLatitude = M_PI;
// 发射角度(弧度为单位),默认0.0。发射的物体均匀地分布在这个锥体上。
colorBallCell.emissionRange = M_PI_4;
colorBallCell.scale = 0.2;
colorBallCell.scaleRange = 0.1;
colorBallCell.scaleSpeed = 0.02;
//粒子的内容
colorBallCell.contents = (id)[[UIImage imageNamed:@"circle_white"] CGImage]; // 一张图片
//颜色变化
colorBallCell.color = [UIColor colorWithRed:0.5 green:0.0 blue:0.5 alpha:1.0].CGColor;
//粒子颜色改变范围
colorBallCell.alphaRange = 0.8f;
colorBallCell.redRange = 1.0f;
colorBallCell.greenRange = 1.0f;
//粒子变化的速度
colorBallCell.blueSpeed = 1.0f;
colorBallCell.alphaSpeed = -0.1;
// 一个发射器可以存放多种的粒子类型,圆形/方形/星形...只需要告诉发射器即可
colorBallLayer.emitterCells = @[colorBallCell];
}
- (void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event{
CGPoint point = [self locationFromTouchEvent:event];
[self setBallInPsition:point];
}
- (void)touchesMoved:(NSSet<UITouch *> *)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"];
// 改变粒子的缩放 0.2->0.5
anim.fromValue = @0.2;
anim.toValue = @0.5f;
anim.duration = 1.0;
//变化模式: 线性变化,动画是匀速发生
anim.timingFunction = [CAMediaTimingFunction functionWithName:kCAMediaTimingFunctionLinear];
//移动粒子的发射位置runloop
//事务性动画,用来包装隐式动画
[CATransaction begin];
[CATransaction setDisableActions:YES];
[self.colorBallLayer addAnimation:anim forKey:nil];
[self.colorBallLayer setValue:[NSValue valueWithCGPoint:position] forKey:@"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(@"雨停了");
// 停止下雨
[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;
// 3.添加到图层上
rainLayer.emitterCells = @[snowCell];
}
@end
五、案例:实现QQ点赞效果
- 点赞按钮放大->缩小:CAKeyFrameAnimation
- 放大之后,释放一个圈的粒子效果:CAEmitterLayer
需要:1.自定义按钮、2.重写按钮选中状态、3.配置动画
#import "ViewController.h"
#import "CCLikeButton.h"
@interface ViewController ()
@end
@implementation ViewController
- (void)viewDidLoad {
[super viewDidLoad];
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
#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) {
// 从没有点击到点击状态 会有爆炸的动画效果
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];
}
- (void)drawRect:(CGRect)rect {
}
@end
网友评论