http://www.jianshu.com/p/197c2257f597
粒子动画
CAEmitterLayer常用属性
CAEmitterLayer并不是杂乱无章地发射粒子的,它发射粒子时的位置,发射的面积和面积对应的几何图形都是可以配置的,可以是一个点,或者一个方形、圆形等等
emitterShape:发射形状
是粒子从什么形状发射出来,它并不是表示粒子自己的形状
- kCAEmitterLayerPoint :点
- kCAEmitterLayerLine:线
- kCAEmitterLayerRectangle :矩形
- kCAEmitterLayerCircle:圆形
- kCAEmitterLayerCuboid :正方体
- kCAEmitterLayerSphere : 球
后两个是3D效果粒子,避而不谈。
emitterPosition:发射位置
在粒子图层上粒子的发射点(支持隐式动画),决定了粒子发射形状的中心点。
emitterSize:发射尺寸
决定了粒子发射形状的大小。
我们用kCAEmitterLayerLine来说明一下。当你的CAEmitterLayer的emitterSize为CGSize(10, 10)时,你的所选择的emitterPosition为CGPoint(10,10)。那么形状为“Line”的CAEmitterLayer就会在如下图紫色的直线上产生粒子,对于“Line”来说,emitterSize的高度是被忽略的。
我们可以这样理解, emitterPosition是所选emitterShape的中心点,例如对于矩形是对角线交点,对于圆形是圆心,对于直线是中点。而 emitterSize则决定了矩形的大小,圆形的大小,直线的长度。这样说应该就够通俗易懂了。
emitterCell会以中心点抽象为一个点,在上述区域内出现。
另外,我们可以将emitterCell的速度相关的属性全部设置为0,你也可以直接注释掉他们,采用默认值,这样我们将会得到一些不会移动的粒子,因为它们没有速度,这样我们能看清楚CAEmitterLayer的形状是什么
kCAEmitterLayerPoint:
kCAEmitterLayerLine:
kCAEmitterLayerRectangle:
kCAEmitterLayerCircle
注意:
emitterShape与emitterSize配合使用
如果是kCAEmitterLayerPoint,emitterSize没有意义。如果是kCAEmitterLayerLine,emitterSize的高度没有意义。如果是其余两种,emitterSize宽高均有意义,如果此时不设置emitterSize,与kCAEmitterLayerPoint模式效果一样。
emitterMode:发射模式
emitterMode的作用是进一步决定发射的区域是在发射形状的哪一部份,当我们看到它的枚举时,也就能大概了解了。
- kCAEmitterLayerPoints : 点发射
- kCAEmitterLayerOutline : 线发射
- kCAEmitterLayerSurface:面发射
- kCAEmitterLayerVolume:容积发射,3D图形的体内
当我们选择Points的时候,粒子会从发射形状的“顶点”发射出来,这里顶点只是一个简单的描述,有些图形不能用顶点来描述的,例如对于圆形来说,“顶点”就是圆心。Outline是指从形状的边界上发射,surface则是从形状的表面上发射,Voloume是相对于3D形状的“球体内”或“立方体内”发射,关于3D形状的问题,以后再说。
注意:
发射方式和emitterShape和emitterSize是配合使用的。当emitterShape和emitterSize共同描绘出点的时候,发射方式使用点发射,选线发射和面发射无意义,依然是点发射的效果。当emitterShape和emitterSize共同描绘出矩形时,发射方式选面发射,如果选点发射会从点发射,选线发射同理。
emitterShape与emitterSize与emitterMode共同配合使用。同选点、线、或者面即可(其实功能有重叠部分,或可不用全部设置)
renderModel:渲染模式,默认值是kCAEmitterLayerUnordered
- kCAEmitterLayerUnordered:无序随机的
- kCAEmitterLayerOldestFirst:最新的在上层出现
- kCAEmitterLayerOldestLast:最新的在下层出现
- kCAEmitterLayerBackToFront :由下层向上层涌动
- kCAEmitterLayerAdditive :叠加显示
渲染模式就是当新的Cell出现的时候,该图层是在上一个Cell的上面还是下面。Additive为叠加模式,叠加的位置颜色会变重。
CAEmitterCell常用属性
CAEmitterLayer决定了粒子系统的发射位置,致于怎么让粒子本身酷炫起来,就需要CAEmitterCell的帮助。
(1)lifetime
粒子在系统上单纯的存在时间,单位是秒,时间到了粒子即会消失。
(2)lifetimeRange
以lifetime为基准的时间跨度,单位是秒。即粒子存在的时间为lifetime为中心点,以lifetimeRange为跨度的随机数,它可以配合lifetime来让粒子生命周期均匀变化,以便可以让粒子的出现和消失显得更加离散。
例如当lifetime=5,lifetimeRange=4时,实际的粒子存在时间为3-7秒。
(3)birthRate
粒子产生数量的决定参数,它表示CAEmitterLayer上每秒产生的粒子数量,birthRate是一个浮点数,即为0.1时表示每十秒产生一个粒子。
(4)contents
contents为cell的内容,类型为id,通常使用图片。用颜色创建图片的方法如下。
-(UIImage*)imageWithColor:(UIColor*)color andSize:(CGSize)size
{
UIGraphicsBeginImageContext(size);
CGContextRef context=UIGraphicsGetCurrentContext();
CGContextSetFillColorWithColor(context, color.CGColor);
CGRect rect=CGRectMake(0, 0, size.width, size.height);
UIBezierPath*bezierPath=[UIBezierPath bezierPathWithRoundedRect:rect cornerRadius:size.width/2.0];
CGContextAddPath(context, bezierPath.CGPath);
CGContextFillPath(context);
CGContextSetFillColorWithColor(context, color.CGColor);
UIImage*theImage=UIGraphicsGetImageFromCurrentImageContext();
UIGraphicsEndImageContext();
return theImage;
}
同样contents也可以直接使用切图,代码如下:
emitterCell.contents = (__bridge id _Nullable)[[UIImage imageNamed:@"imageName"] CGImage];
使用bridge桥接NS类与CF类。
注意:contents不能为空,不能使用图片名不存在的图片。且图片大小决定cell的初始大小。
(5)color
color作为cell的背景色,与contents配合使用。
如果contents的颜色以[UIColor colorWithRed:1 green:1 blue:1 alpha:1]来创建,并且我们cell的color传入[UIColor colorWithRed:0.5 green:1 blue:1 alpha:1],那么最终颜色为[UIColor colorWithRed:0.5 green:1 blue:1 alpha:1]。
同样如果使用蓝色的contents图片,黄色的color,最终会得到绿色图片。
color和contents共同作用得出最终颜色。所以当使用contents图片控制cell时,color使用白色;当使用color控制cell时,contents使用纯白色图片(whiteColor,不可使用clearColor)。
(6)redRange,greenRange,blueRange
用于随机生成颜色,该属性为粒子生成时的颜色的容差范围,值为0~1。
例如redRange为0.1,那么当你的粒子的color对应的rgb为(10,255,255)。那么在其它值取默认值的前提下,这个粒子在CAEmitterLayer上发射出来的时候,它的rgb中的red component会均匀分布在 10正负0.1*255之间,即[0,35],颜色值不能为负。
(7)redSpeed,greenSpeed,blueSpeed
用于颜色逐步变化,属性为粒子颜色的变化速度,它的取值范围也是0~1。表示每秒钟的颜色变化率,乘以粒子生命周期即为变化范围。
例如你的粒子的颜色是rgb(0,255,255),并且你的粒子的redRange也为0 ,这表示你的粒子被发射出来的时候,它的颜色值中的红色值总是为0 。你的粒子的生命周期lifetime为10,即粒子可以存在10秒,10秒后会从layer上移除。那么当你的redSpeed取值为0.1时,你将会看到你的粒子的红色值每秒增加0.1*255,这个过程是连续不断变化,外观上看就是你的粒子越来越接近白色。当粒子到达生命结束的时候也就是10秒的时候,你的粒子的颜色也恰好变成了rgb(255,255,255)。
(8)alphaRange和alphaSpeed
同(6)、(7),控制透明度的初始范围和变化速率,可以通过此属性控制粒子的渐隐渐现。
(9) emissionLongitude
emissionLongtitude决定了粒子飞行方向跟水平坐标轴(x轴)之间的夹角,默认是0,即沿着x轴向右飞行。顺时针方向是正向,例如emissionLongtitude为0 ,则粒子顺着x轴飞行,如果你想让你的粒子发射出来后,沿着y轴向下飞行,那么emissionLongtitude就应该设置为PI/2。即90度,正如我所说的那样,顺时针方向是正方向。如果你想让你的粒子沿着y轴向上飞行,你可以将emissionLongtitude设置为 3*PI/2也可设置为 -PI/2。下图就简单展示了emissionLongtitude的几个典型取值,图中绿色箭头所指的方向就是当emissionLongtitude为箭头对应角度时的粒子飞行方向:
(10)emissionRange
emissionRange则决定了粒子的发散范围,同样是一个弧度值(radians),表示粒子在沿着emissionLongtitude方向所形成的顶角为2倍emissionRange的圆锥范围内发散。我们看例图:我们把emisstionLongtitude设置为-PI/2,让粒子向上飞行,并且让emissionRange为PI/4。那么按照上面的说法,我们应该能得到一个向上的,并且顶角为2 * PI/4的圆锥,结果应该如下图:
(11) emissionLatitude
粒子Z轴上的发射角度,用于三维立体效果,可以不做设置。与emissionLongitude原理相同,默认为0,在平面上向下,为PI时向上。为PI/2是垂直屏幕向外,3*PI/2时垂直屏幕向里,在屏幕上表现为粒子不动。
emissionLatitude的值为与平面的夹角,当存在值时,我们所看到的粒子运动距离实际上为该边长在平面上的垂直投影(直角边),小于velocity与lifeTime的乘积(斜边)。
emissionRange的值可能也作用于emissionLatitude,这样所得出的粒子出没区域不再是单纯扇形(拥有更大的角度,更短的半径),更具有离散性。
(12)velocity
粒子的运动速度,配合lifeTime得出粒子活动区域。默认为0,即粒子不运动。
velocity可以为负值,即沿反方向移动。
(13)velocityRange
粒子运动速度范围。例如当velocity为100,velocityRange为200时,最终速度范围为0~200。
(14)xAcceleration yAcceleration zAcceleration
这3个属性分别定义了3个坐标轴上的加速度,它们代表了不同坐标轴方向上的每秒的速度增量,与物理学上保持一致。即当加速度为正数时加速,为负数时减速,当速度减为负数时反向加速移动。
(15)spin,spinRange
粒子的自转。粒子的自转是以弧度制来计算的,表示每秒钟粒子自转的弧度数,2PI为每秒自转一周。例如你的粒子的生命周期就是10秒,那么你想让你的粒子在10秒内刚好自转1周,那么spin为2PI/10。另外,当spin为正数的时候,粒子是顺时针旋转的,为负数的话就是逆时针选转了。 spinRange就不多说了,跟其它range的意义和作用是一样的,让自转速度离散。
(16)scale,scaleRange,scaleSpeed
粒子的缩放。scale与scaleRange的值为0~1,可以更改粒子初始的大小。scaleSpeed表示粒子的变化速率,与以上其他属性含义相同。
(17)subCell
可以指定一个subcell作为cell的cells列表成员,完成粒子在完成动画消失时开始下一个动画。
一个简单的下雨动画代码
- (instancetype)initWithFrame:(CGRect)frame
{
self = [super initWithFrame:frame];
if (self) {
[self addSubview:self.backgroundImageView];
self.backgroundColor = [UIColor clearColor];
self.layer.masksToBounds = YES;
dispatch_async(dispatch_get_main_queue(), ^{
[self.layer addSublayer:self.emitter];
});
}
return self;
}
- (UIImageView *)backgroundImageView
{
if (!_backgroundImageView) {
_backgroundImageView = [[UIImageView alloc] initWithFrame:self.bounds];
_backgroundImageView.image = [UIImage imageNamed:@"bg_rain"];
}
return _backgroundImageView;
}
- (CAEmitterLayer *)emitter
{
if (!_emitter) {
_emitter = ({
CAEmitterLayer *emitter = [CAEmitterLayer layer];
emitter.backgroundColor = [UIColor clearColor].CGColor;
emitter.emitterPosition = CGPointMake(self.bounds.size.width, -40);
emitter.emitterSize = CGSizeMake(self.bounds.size.width * 1.5, 80);
emitter.emitterMode = kCAEmitterLayerSurface;
emitter.emitterShape = kCAEmitterLayerRectangle;
CAEmitterCell *midEmitterCell = [self midEmitterCell];
CAEmitterCell *smallEmitterCell = [self smallEmitterCell];
CAEmitterCell *largeEmitterCell = [self largeEmitterCell];
//add the cell to the emitter layer
emitter.emitterCells = @[ midEmitterCell, smallEmitterCell, largeEmitterCell ];
emitter;
});
}
return _emitter;
}
- (CAEmitterCell *)largeEmitterCell
{
CAEmitterCell *emitterCell = [CAEmitterCell emitterCell];
emitterCell.contents = (id)[[UIImage imageNamed:@"rain_large"] CGImage];
emitterCell.name = @"rain_large";
UIColor *color = [UIColor colorWithWhite:1.0 alpha:1.0];
emitterCell.color = color.CGColor;
emitterCell.birthRate = LargeRainrate;
emitterCell.lifetime = lifeTime(2.1);
emitterCell.lifetimeRange = 0;
emitterCell.velocity = 150;
emitterCell.velocityRange = 50;
emitterCell.emissionLongitude = 2;
emitterCell.emissionRange = 0;
emitterCell.xAcceleration = -1;
emitterCell.yAcceleration = 20;
emitterCell.alphaRange = 0.8;
emitterCell.alphaSpeed = 0.1;
emitterCell.scale = 0.6;
emitterCell.scaleRange = 0.2;
emitterCell.scaleSpeed = 0.0;
CAEmitterCell *subCell = [self waterCirleCell];
emitterCell.emitterCells = @[ subCell ];
return emitterCell;
}
- (CAEmitterCell *)midEmitterCell
{
CAEmitterCell *emitterCell = [CAEmitterCell emitterCell];
emitterCell.contents = (id)[[UIImage imageNamed:@"rain_mid"] CGImage];
emitterCell.name = @"rain_mid";
UIColor *color = [UIColor colorWithWhite:1.0 alpha:1.0];
emitterCell.color = color.CGColor;
emitterCell.birthRate = MidRainrate;
emitterCell.lifetime = lifeTime(2.0);
emitterCell.lifetimeRange = 0;
emitterCell.velocity = 150;
emitterCell.velocityRange = 50;
emitterCell.emissionLongitude = 2;
emitterCell.emissionRange = 0;
emitterCell.xAcceleration = -1;
emitterCell.yAcceleration = 25;
emitterCell.alphaRange = 0.8;
emitterCell.alphaSpeed = -0.1;
emitterCell.scale = 0.6;
emitterCell.scaleRange = 0.2;
emitterCell.scaleSpeed = 0.0;
return emitterCell;
}
- (CAEmitterCell *)smallEmitterCell
{
CAEmitterCell *emitterCell = [CAEmitterCell emitterCell];
emitterCell.contents = (id)[[UIImage imageNamed:@"rain_small"] CGImage];
emitterCell.name = @"rain_small";
UIColor *color = [UIColor colorWithWhite:1.0 alpha:1.0];
emitterCell.color = color.CGColor;
emitterCell.birthRate = SmallRainrate;
emitterCell.lifetime = lifeTime(2.0);
emitterCell.lifetimeRange = 0;
emitterCell.velocity = 200;
emitterCell.velocityRange = 50;
emitterCell.emissionLongitude = 2;
emitterCell.emissionRange = 0;
emitterCell.xAcceleration = -1;
emitterCell.yAcceleration = 30;
emitterCell.alphaRange = 0.6;
emitterCell.alphaSpeed = -0.2;
emitterCell.scale = 0.6;
emitterCell.scaleRange = 0.2;
emitterCell.scaleSpeed = 0.0;
return emitterCell;
}
- (CAEmitterCell *)waterCirleCell
{
//create new emitter cell
CAEmitterCell *emitterCell = [CAEmitterCell emitterCell];
emitterCell.beginTime = lifeTime(2.05);
//粒子图片
emitterCell.contents = (__bridge id _Nullable)[[UIImage imageNamed:@"circle_small"] CGImage];
//粒子源名称
emitterCell.name = @"circle_small";
//get the particles start color
UIColor *color = [UIColor whiteColor];
emitterCell.color = color.CGColor;
//copy all the settings to the emitter cell
//产生速率
emitterCell.birthRate = 1;
//生命周期
emitterCell.lifetime = 2.4;
//透明度
emitterCell.alphaRange = 0.8;
//透明度变化速度
emitterCell.alphaSpeed = -0.3;
//缩放
emitterCell.scale = 0.4;
emitterCell.scaleRange = 0.3;
emitterCell.scaleSpeed = 0.4;
return emitterCell;
}