CAEmitterLayer动画的开始和结束
有个需求,要求模仿微信做表情下雨的动画,一开始想用CAEmitterLayer,实现的代码如下:
//期望:显示特效五秒后结束特效
UIImage *image = [UIImage imageNamed:@"snow_white"];
CGRect endRect = self.view.frame;
UIView *layerView = [[UIView alloc]initWithFrame:endRect];
//方便看到有view增加到里面
layerView.backgroundColor = [UIColor colorWithRed:1 green:0 blue:0 alpha:0.1];
layerView.userInteractionEnabled = NO;
CAEmitterLayer * fireworksLayer = [CAEmitterLayer layer];
[layerView.layer addSublayer:fireworksLayer];
[self.view addSubview:layerView];
[layerView.layer addSublayer:fireworksLayer];
fireworksLayer.emitterPosition = CGPointMake(endRect.size.width * 0.5, -image.size.height / 2); // 这个position表示的是粒子产生的位置,注意是图片中心位置的初始值,而不是(x,y)
fireworksLayer.emitterSize = CGSizeMake(endRect.size.width - image.size.width / 2 * 2, 0.f); // 粒子产生的随机区域,如果要让生成的粒子图片不过左右屏幕边缘,记住给左右两边留点空间
fireworksLayer.emitterMode = kCAEmitterLayerOutline;
fireworksLayer.emitterShape = kCAEmitterLayerLine;
fireworksLayer.renderMode = kCAEmitterLayerAdditive;
fireworksLayer.birthRate = 1;
// 粒子
CAEmitterCell * cell = [CAEmitterCell emitterCell];
cell.birthRate = 1.f;//每一秒产生的粒子个数(实际数量和上面的birthRate相乘)
cell.lifetime = 5.f;//粒子产生到消失为5秒
cell.velocity = -(endRect.size.height + image.size.height / 2 * 2) / 5;
cell.contents = (id)[image CGImage];
fireworksLayer.emitterCells = @[cell];
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, NSEC_PER_SEC * 5), dispatch_get_main_queue(), ^{
//移除layer
if (layerView.superview) {
[layerView removeFromSuperview];
}
});
这时候我们能看到效果如此:

这个时候其实我们做到了以下几点
- view显示5秒并删除
- 雪花从上向下降落,而且速度是匀速的view高度/5秒
- 雪花每秒产生1颗
- 雪花在不超出左右边缘之内随机产生
但是这个效果最大的缺陷在于:雪花的产生和结束都十分的突兀,还需要实现的效果应该是:
- 效果开始时,雪花正好从顶部开始落下
- view即将移除的时候,雪花不再生成
- view移除的时候,最后一片雪花刚好落出屏幕
所以我们的优化也从三个方面进行。
动画起始时间点 beginTime
如果直接查看CAEmitterLayer.h文件,并不能发现beginTime这个属性,甚至其父类CALayer也找不到,直到找到协议CAMediaTiming才能找到。
// CALayer.h
@interface CALayer : NSObject <NSSecureCoding, CAMediaTiming>
...
@end
// CAMediaTiming.h
@protocol CAMediaTiming
...
/* The begin time of the object, in relation to its parent object, if
* applicable. Defaults to 0. */
@property CFTimeInterval beginTime;
...
@end
所以我们设置beginTime就可以规定我们想让layer开始动画的时间,但是不应该设置为0(本来就是默认为0),而是CACurrentMediaTime(),也就是现在。
CACurrentMediaTime,文档里面告诉我们这是一个以秒为单位的当前的absolute time,属性为CFTimeInterval(也是double), 在这里“绝对时间”不是某度百科里的提到的时空观或者某个历法,而是mach_absolute_time()——即基于系统启动后的时钟嘀嗒数转换单位为秒的结果,与常见的时间戳[[NSDate date]timeIntervalSince1970]更是没半毛钱没有关系。NSLog(@"%lf %lf",[[NSDate date]timeIntervalSince1970], CACurrentMediaTime());
//在某个时空输出为:1646246718.232236 197452.774114
回到问题来,我们只要设置好beginTime,就可以让动画从“现在”开始播放,而默认的0表示的是在“万物之始”开始播放,也难怪我们刚才从屏幕上开始看到的动画不是从零开始的。
fireworksLayer.beginTime = CACurrentMediaTime();
让雪花停止生成
我们需要在粒子开始运动之后,让粒子生成速度更改为0个/秒,首先明确的是,每秒生成粒子实际数量是fireworksLayer.birthRate * cell.birthRate,那么我们挠挠头就可以添加这段代码。
//细化需求为1s-3s是不断生成粒子,3s生成最后一个粒子后不再生成粒子
.....
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, NSEC_PER_SEC * 3), dispatch_get_main_queue(), ^{
fireworksLayer.birthRate = 0;//动画在运动过程中要修改参数的话,可以修改layer的参数,试过在这个过程中只改cell参数不会成功
});
调整速度,让最后一颗粒子正好滚出屏幕时移除view
这里为了方便说明,所以本次只让雪花在y轴上做匀速运动,在x轴上相对静止(不左右移动),也是提醒各位,粒子产生的位置不是图片初始的xy坐标值,而是中心点,这既是是我代码中设置fireworksLayer.emitterPosition会附加图片一半的高度(开始生成粒子时粒子下沿要在view上端)的原因,也是为什么计算速度所用到的总路程时,要加上图片高度一半的两倍(粒子上沿要到view下端才算结束)的原因。
cell.velocity = -(endRect.size.height + image.size.height / 2 * 2) / 2; //正好最后一朵花开始的时候其图片下沿在屏幕顶部,结束的时候其图片上落到屏幕顶部
加上文章内提交优化后的代码:
//期望:显示特效五秒后结束特效
UIImage *image = [UIImage imageNamed:@"snow_white"];
CGRect endRect = self.view.frame;
UIView *layerView = [[UIView alloc]initWithFrame:endRect];
//方便看到有view增加到里面
layerView.backgroundColor = [UIColor colorWithRed:1 green:0 blue:0 alpha:0.1];
layerView.userInteractionEnabled = NO;
CAEmitterLayer * fireworksLayer = [CAEmitterLayer layer];
[layerView.layer addSublayer:fireworksLayer];
[self.view addSubview:layerView];
[layerView.layer addSublayer:fireworksLayer];
fireworksLayer.emitterPosition = CGPointMake(endRect.size.width * 0.5, -image.size.height / 2); // 这个position表示的是粒子产生的位置,注意是图片中心位置的初始值,而不是(x,y)
fireworksLayer.emitterSize = CGSizeMake(endRect.size.width - image.size.width / 2 * 2, 0.f); // 粒子产生的随机区域,如果要让生成的粒子图片不过左右屏幕边缘,记住给左右两边留点空间
fireworksLayer.emitterMode = kCAEmitterLayerOutline;
fireworksLayer.emitterShape = kCAEmitterLayerLine;
fireworksLayer.renderMode = kCAEmitterLayerAdditive;
fireworksLayer.birthRate = 1;
fireworksLayer.beginTime = CACurrentMediaTime();
// 粒子
CAEmitterCell * cell = [CAEmitterCell emitterCell];
cell.birthRate = 1.f;//每一秒产生的粒子个数(实际数量和上面的birthRate相乘)
cell.lifetime = 5.f;//粒子产生到消失为5秒
cell.velocity = -(endRect.size.height + image.size.height / 2 * 2) / 2; //正好最后一朵花开始的时候其图片下沿在屏幕顶部,结束的时候其图片上落到屏幕顶部
cell.contents = (id)[image CGImage];
fireworksLayer.emitterCells = @[cell];
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, NSEC_PER_SEC * 3), dispatch_get_main_queue(), ^{
fireworksLayer.birthRate = 0;//动画在运动过程中要修改参数的话,可以修改layer的参数,试过在这个过程中只改cell参数不会成功
});
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, NSEC_PER_SEC * 5), dispatch_get_main_queue(), ^{
//移除layer
if (layerView.superview) {
[layerView removeFromSuperview];
}
});
效果图:

CAEmitterLayer动画的开始和结束的更多相关文章
- 原生js判断css动画结束 css 动画结束的回调函数
原文:原生js判断css动画结束 css 动画结束的回调函数 css3 的时代,css3--动画 一切皆有可能: 传统的js 可以通过回调函数判断动画是否结束:即使是采用CSS技术生成动画效果,Jav ...
- 原生js判断css3动画过度(transition)结束 transitionend事件 以及关键帧keyframes动画结束(animation)回调函数 animationEnd 以及 css 过渡 transition无效
上图的 demo 主要讲的 是 css transition的过渡回调函数transitionend事件: css3 的时代,css3--动画 一切皆有可能: 传统的js 可以通过回调函数判断动画 ...
- IOS动画(Core Animation)总结 (参考多方文章)
一.简介 iOS 动画主要是指Core Animation框架.官方使用文档地址为:Core Animation Guide. Core Animation是IOS和OS X平台上负责图形渲染与动画的 ...
- iOS:动画(18-10-15更)
目录 1.UIView Animation 1-1.UIView Animation(基本使用) 1-2.UIView Animation(转场动画) 2.CATransaction(Layer版的U ...
- iOS开发-动画总结
一.简介 IOS 动画主要是指Core Animation框架.官方使用文档地址为:Core Animation Guide.Core Animation是IOS和OS X平台上负责图形渲染与动画的基 ...
- CSS 3学习——animation动画
以下内容根据官方文档翻译以及自己的理解整理. 1. 介绍 本方案介绍动画(animations).通过动画,开发者可以将CSS属性值的变化指定为一个随时间变化的关键帧(keyframes)的集合.在 ...
- 虾扯蛋:Android View动画 Animation不完全解析
本文结合一些周知的概念和源码片段,对View动画的工作原理进行挖掘和分析.以下不是对源码一丝不苟的分析过程,只是以搞清楚Animation的执行过程.如何被周期性调用为目标粗略分析下相关方法的执行细节 ...
- app引导页(背景图片切换加各个页面动画效果)
前言:不知不觉中又加班到了10点半,整个启动页面做了一天多的时间,一共有三个页面,每个页面都有动画效果,动画效果调试起来麻烦,既要跟ios统一,又要匹配各种不同的手机,然后产品经理还有可能在中途改需求 ...
- Android开发学习——动画
帧动画> 一张张图片不断的切换,形成动画效果* 在drawable目录下定义xml文件,子节点为animation-list,在这里定义要显示的图片和每张图片的显示时长 ...
- SVG动画
动画原理 SVG动画,就是元素的属性值关于时间的变化. 如下图来说,元素的某个属性值的起始值(from)到结束值(to)在一个时间段(duration)根据时间函数(timing-function)计 ...
随机推荐
- 安装petalinux
1.petalinux工具下载,下载地址: https://china.xilinx.com/support/download/index.html/content/xilinx/zh/downloa ...
- K8s集群中部署SpringCloud在线购物平台(二)
三.harbor简介 harbor 是私有镜像仓库,用来存储和分发镜像的 docker 还有一个官方的镜像仓库 docker hub,免费用户只能简单的使用,创建一个私有镜像仓库,存储 镜像,付费用户 ...
- Git——分支管理(2)
Git--分支管理(2) 提示:图床在国外且动图比较多的情况下,需要时间加载. 目录: 目录 Git--分支管理(2) 提示:图床在国外且动图比较多的情况下,需要时间加载. 目录: Git基础 Git ...
- java stream数据分组 stream().collect(groupingBy
List<Map<String, Object>> foreList = forecastDataDao.getcityforeresults(param); Map<S ...
- SpringBoot3集成WebSocket
标签:WebSocket,Session,Postman. 一.简介 WebSocket通过一个TCP连接在客户端和服务器之间建立一个全双工.双向的通信通道,使得客户端和服务器之间的数据交换变得更加简 ...
- C语言:算法题判断是否有效字符({[]})---括号
给定一个只包括 '(',')','{','}','[',']'的字符串 s ,判断字符串是否有效. 有效字符串需满足: 左括号必须用相同类型的右括号闭合. ...
- .NET 代理模式(二) 动态代理-DispatchProxy
前言 我们都知道,在.NET中实现动态代理AOP有多种方案,也有很多框架支持,但大多框架的实现原理都是通过Emit配合Activator一起使用,从IL级别上实现动态代理. 其实在.NET中有一个更为 ...
- 5款.NET开源、免费、功能强大的图表库
LiveCharts2 LiveCharts2是一个.NET开源(MIT License).简单.灵活.交互式且功能强大的.NET图表.地图和仪表,现在几乎可以在任何地方运行如:Maui.Uno Pl ...
- MYSQL造数据占用临时表空间
在MySQL中,临时表空间通常用于存储如ORDER BY.GROUP BY.DISTINCT.UNION.JOIN等操作中产生的临时数据.当这些操作的数据集太大而无法在内存中完成时,MySQL会使用磁 ...
- Javascript---时间加秒数得到新的时间
<script> // 当前时间:curTime var curTime = new Date("2023-12-10 17:32:46"); // 当前时间加上XXX ...