iOS-仿微信蛋糕雨效果CAKeyframeAnimation
2022-03-30 本文已影响0人
丿小七
模仿微信蛋糕雨的动画效果,因为动画做的少,查了些资料,开始觉得粒子效果的动画很炫,简单做了个demo后发现他有些细节不好控制,像微信的蛋糕雨,停止的时候是慢慢停的,而粒子效果的停止跟出现,都是直接铺满或者直接全部停止的。最后还是考虑了用帧动画的形式去实现蛋糕雨的效果。简单记录下自己的使用效果跟代码片段。
-
CAKeyframeAnimation
values
决定移动轨迹
duration
移动轨迹需要的时长
timingFunction
定义动画在其持续时间(或在一个关键帧的持续时间) -
效果
效果参考.gif
CAKeyframeAnimation *framMoveA = [[CAKeyframeAnimation alloc] init];
framMoveA.keyPath = @"position";
NSMutableArray *points = [NSMutableArray array];
UInt32 sW = (UInt32)ceilf(self.view.frame.size.width);
UInt32 sH = (UInt32)ceilf(self.view.frame.size.height);
// 随机分布在屏幕内
uint32_t randX1 = arc4random_uniform(sW);
uint32_t randX2 = arc4random_uniform((sW*3)/4);
uint32_t randX3 = arc4random_uniform(sW/2);
uint32_t randX4 = arc4random_uniform(sW);
uint32_t randX5 = arc4random_uniform((sW*3)/4);
NSValue *value1 = [NSValue valueWithCGPoint:CGPointMake(randX1, 0)];
NSValue *value2 = [NSValue valueWithCGPoint:CGPointMake(randX2, sH/4)];
NSValue *value3 = [NSValue valueWithCGPoint:CGPointMake(randX3, sH/2)];
NSValue *value4 = [NSValue valueWithCGPoint:CGPointMake(randX4, sH*3/4)];
NSValue *value5 = [NSValue valueWithCGPoint:CGPointMake(randX5, sH)];
[points addObject:value1];
[points addObject:value2];
[points addObject:value3];
[points addObject:value4];
[points addObject:value5];
framMoveA.values = points;
framMoveA.duration = animationTime;
framMoveA.repeatCount = 1;
CAMediaTimingFunction *timingFuc = [CAMediaTimingFunction functionWithName:kCAMediaTimingFunctionLinear];
framMoveA.timingFunction = timingFuc;
[self.moveLayer addAnimation:framMoveA forKey:@"move"];
- 整体代码参考
//
// ViewController.m
// animal
//
// Created by xxx on 2022/3/29.
//
#import "ViewController.h"
@interface ViewController ()
@property (nonatomic, strong) CAEmitterLayer *emitterL; /**< 粒子动画层 */
@property (nonatomic, strong) NSTimer *timer; /**< timer */
@property (nonatomic, strong) CALayer *moveLayer; /**< */
@property (nonatomic, assign) NSInteger countTime; /**< 动画时长 计时结束自动停止 */
@end
#define animationTime 8 // 动画执行时间,从开始掉落到结束中间的运行时间。
#define animationSpeed 0.5 // 执行间隔时间 0.5秒执行一次
@implementation ViewController
- (void)viewDidLoad {
[super viewDidLoad];
}
- (void)viewDidAppear:(BOOL)animated {
[super viewDidAppear:animated];
}
#pragma mark - ============== view animation ==============
// 定时器循环创建需要掉落的图片
- (NSTimer *)timer {
if (!_timer) {
_timer = [NSTimer timerWithTimeInterval:animationSpeed target:self selector:@selector(handleTimerEvent) userInfo:nil repeats:YES];
}
return _timer;
}
- (void)handleTimerEvent {
if (self.countTime < 0) {
[self handleViewAnimationClick:nil];
}else {
self.countTime --;
UIImageView *imgV = [[UIImageView alloc] initWithImage:[UIImage imageNamed:@"testImg"]];
imgV.frame = CGRectMake(0, 0, 40, 40);
self.moveLayer = [[CALayer alloc] init];
self.moveLayer.bounds = imgV.frame;
self.moveLayer.anchorPoint = CGPointMake(0, 0);
self.moveLayer.position = CGPointMake(0, -40);
self.moveLayer.contents = (__bridge id _Nullable)(imgV.image.CGImage);
[self.view.layer addSublayer:self.moveLayer];
[self handleAnimation:self.moveLayer];
}
}
// 大概类似实现从上到下,整个屏幕内的随机范围掉落效果
- (void)handleAnimation:(CALayer *)moveLayer {
CAKeyframeAnimation *framMoveA = [[CAKeyframeAnimation alloc] init];
framMoveA.keyPath = @"position";
NSMutableArray *points = [NSMutableArray array];
UInt32 sW = (UInt32)ceilf(self.view.frame.size.width);
UInt32 sH = (UInt32)ceilf(self.view.frame.size.height);
uint32_t randX1 = arc4random_uniform(sW);
uint32_t randX2 = arc4random_uniform((sW*3)/4);
uint32_t randX3 = arc4random_uniform(sW/2);
uint32_t randX4 = arc4random_uniform(sW);
uint32_t randX5 = arc4random_uniform((sW*3)/4);
NSValue *value1 = [NSValue valueWithCGPoint:CGPointMake(randX1, 0)];
NSValue *value2 = [NSValue valueWithCGPoint:CGPointMake(randX2, sH/4)];
NSValue *value3 = [NSValue valueWithCGPoint:CGPointMake(randX3, sH/2)];
NSValue *value4 = [NSValue valueWithCGPoint:CGPointMake(randX4, sH*3/4)];
NSValue *value5 = [NSValue valueWithCGPoint:CGPointMake(randX5, sH)];
[points addObject:value1];
[points addObject:value2];
[points addObject:value3];
[points addObject:value4];
[points addObject:value5];
framMoveA.values = points;
framMoveA.duration = animationTime;
framMoveA.repeatCount = 1;
CAMediaTimingFunction *timingFuc = [CAMediaTimingFunction functionWithName:kCAMediaTimingFunctionLinear];
framMoveA.timingFunction = timingFuc;
[self.moveLayer addAnimation:framMoveA forKey:@"move"];
}
- (IBAction)handleViewAnimationClick:(UIButton *)sender {
NSInteger tag = sender ? sender.tag : 0;
switch (tag) {
case 100: // 开始动画
{
self.countTime = animationTime/animationSpeed;
[[NSRunLoop mainRunLoop] addTimer:self.timer forMode:NSDefaultRunLoopMode];
[self.timer fire];
}
break;
case 101: // 结束动画
{
[self.timer invalidate];
}
break;
default:
[self.timer invalidate];
self.timer = nil;
break;
}
}
#pragma mark - ============== CAEmitterLayer ==============
- (IBAction)stop:(UIButton *)sender {
[self.emitterL removeAllAnimations];
[self.emitterL removeFromSuperlayer];
}
- (IBAction)animationFirstBtnClick:(UIButton *)sender {
[self tmyHandleInitAnimation];
}
- (void)tmyHandleInitAnimation {
CGFloat wH = self.view.frame.size.height;
NSMutableArray *cellArr = [NSMutableArray array];
for (NSInteger i = 0; i< 2; i ++) {
CAEmitterCell *cell2 = [[CAEmitterCell alloc] init];
cell2.velocity = wH/5;
cell2.velocityRange = (wH / 5)*I;
cell2.scale = 2;
cell2.scaleRange = 1;
cell2.emissionLongitude = M_PI_2; // 经度 竖直方向
cell2.emissionRange = M_PI_2 / 4;
cell2.lifetime = 12;
cell2.lifetimeRange = 0;
// cell2.spin = M_PI_2; // 图片是否旋转角度
// cell2.spinRange = M_PI_2 / 2; // 旋转角度范围
cell2.birthRate = 1;
cell2.contents = (__bridge id _Nullable)([UIImage imageNamed:@"testImg"].CGImage);
[cellArr addObject:cell2];
}
self.emitterL.emitterCells = cellArr;
[self.view.layer addSublayer:self.emitterL];
}
/** 粒子发射器层。
每个发射器有一个细胞阵列,细胞定义如何粒子是由层发出和渲染的。
粒子系统受层的时间影响。仿真开始在图层的beginTime。
粒子绘制在背景颜色和边界层。
*/
- (CAEmitterLayer *)emitterL {
if (!_emitterL) {
_emitterL = [[CAEmitterLayer alloc] init];
_emitterL.emitterPosition = CGPointMake(self.view.center.x, -self.view.frame.size.height);
_emitterL.preservesDepth = YES;
_emitterL.emitterSize = self.view.frame.size;
// _emitterL.lifetime = 10;
}
return _emitterL;
}
具体 CAKeyframeAnimation
、CAEmitterLayer
的属性可以参考以下文章。