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)计 ...
随机推荐
- [Caddy2] URL访问路径的重定向和重写规则 (redir/rewrite 指令)
当我们在规划网站路径时,为了保留搜索引擎收录 避免404的同时做到升级,常用到重定向跳转和URL重写. 重定向(redirect) 在 Caddy 中为 redir 指令. https://caddy ...
- 通过 KoP 将 Kafka 应用迁移到 Pulsar
通过 KoP 将 Kafka 应用迁移到 Pulsar 版权声明:原文出自 https://github.com/streamnative/kop ,由 Redisant 进行整理和翻译 目录 通过 ...
- (数据科学学习手札160)使用miniforge代替miniconda
本文已收录至我的Github仓库https://github.com/CNFeffery/DataScienceStudyNotes 1 简介 大家好我是费老师,conda作为Python数据科学领域 ...
- RocketMQ 事件驱动:云时代的事件驱动有啥不同?
前言: 从初代开源消息队列崛起,到 PC 互联网.移动互联网爆发式发展,再到如今 IoT.云计算.云原生引领了新的技术趋势,消息中间件的发展已经走过了 30 多个年头. 目前,消息中间件在国内许多行业 ...
- WEB服务与NGINX(22)- nginx的七层负载均衡功能
目录 1. NGINX实现负载均衡功能 1.1 nginx负载均衡概述 1.2 nginx实现http的负载均衡 1.2.1 nginx基于http负载均衡场景配置 1.2.1.1 负载均衡相关参数 ...
- 如何去掉WordPress分类目录url链接中的category,如何处理生成的作者链接
一个新网站需要结合SEO,才能做成一个优秀的网站, ◆◆◆ 关于WordPress的分类目录url中含有category的处理办法: 1,如果是新网站这些设置需要提前做,方便以后做SEO 1.修改固定 ...
- C++ placement new学习
通常创建对象使用new操作,但这样无法指定在具体某一块内存开辟空间创建对象.而如果 可以指定开辟空间的内存位置,我们可以编写内存池高效的复用同一个内存位置,这样可以避免系统频繁申请可用内存 所占用的时 ...
- 物联网平台选型葵花宝典:盘点开源、SaaS及通用型平台的优劣对比
随着工业物联网领域和智慧物联领域的发展,大大小小的物联项目和物联场景需求层出不穷,物联网平台作为技术底座型软件,是不可或缺的项目地基. 市场需求下,物联网平台提供商越来越多,"打地基&quo ...
- Spring IoC注解式开发无敌详细(细节丰富)
1. Spring IoC注解式开发无敌详细(细节丰富) @ 目录 1. Spring IoC注解式开发无敌详细(细节丰富) 每博一文案 2. 注解回顾 3. Spring 声明Bean的注解 3.1 ...
- vue多页面应用
多页面应用本身和单页面应用没什么差别,无非是多了几个入口点. 入口点多的话,还可以写个函数扫描路径取添加入口点. 比较让人好奇的是路径的问题.我们要开发的时候要体现目录层级接口,所以入口文件是一层套一 ...