版本记录
版本号 | 时间 |
---|---|
V1.0 | 2019.01.26 星期六 |
前言
如果你细看了我前面写的有关动画的部分,就知道前面介绍了
CoreAnimation
、序列帧以及LOTAnimation
等很多动画方式,接下来几篇我们就以动画示例为线索,进行动画的讲解。部分相关代码已经上传至GitHub - 刀客传奇。感兴趣的可以看我写的前面几篇。
1. 动画示例(一) —— 一种外扩的简单动画
2. 动画示例(二) —— 一种抖动的简单动画
3. 动画示例(三) —— 仿头条一种LOTAnimation动画
4. 动画示例(四) —— QuartzCore之CAEmitterLayer下雪❄️动画
5. 动画示例(五) —— QuartzCore之CAEmitterLayer烟花动画
6. 动画示例(六) —— QuartzCore之CAEmitterLayer、CAReplicatorLayer和CAGradientLayer简单动画
7. 动画示例(七) —— 基于CAShapeLayer图像加载过程的简单动画(一)
8. 动画示例(八) —— UIViewController间转场动画的实现 (一)
9. 动画示例(九) —— 一种复杂加载动画的实现 (一)
10. 动画示例(十) —— 一种弹性动画的实现 (一)
11. 动画示例(十一) —— 一种基于UIView的Spring弹性动画的实现 (一)
功能需求
需要做不规则形状的注入的动画,并且根据不同的百分比涨的高度也不同,就像一个不规则水杯进行注水。
具体动画示例如下所示:
大家可以看见这个动画分为以下几个部分:
- 1)红心放大
- 2)放大结束后进行进行翻转为空心,并恢复原大小
- 3)空心然后就根据给定值和最大值进行动画,每次都是从0上涨到指定高度
功能实现
1. 方案讨论
首先我们一起看下方案
UIView动画
首先,我们最常见的是UIView动画,但是努力进行了尝试,UIView动画不能做注入的那种效果,只能做移动和扩大等类似的逻辑。
mask结合CADisplayLink
接着我们只能换一个方法,具体思路就是在同一个位置放两个UIImageView,下面的是一个满心,上面的是一个空心,然后我们首先要做的就是给下面的满心的layer放置一个mask,实例化一个CAShapeLayer
,设置为下面的满心的UIImageView
的layer的mask。
这样可以保证的是下面的满心看不见了,只有上面的那个空心的UIImageView。然后我们接着做什么呢,就是实例化CADisplayLink
,利用它的action进行动画,慢慢的将那个mask向上移动,逐渐露出下面的满心。这样,给我们的感觉就是上面的空心在慢慢的往上涨。具体涨的高度就根据业务逻辑,设定一个最大值和当前值,将最大值和心的这个image的高度挂钩,算出来的数值就是mask要往上移动的高度。这个动画是在CADisplayLink
的action里面做的。
2. 方案实现
首先看一下sb中的视图
下面就是直接看代码了。
1. ViewController.m
#import "ViewController.h"
@interface ViewController ()
@property (weak, nonatomic) IBOutlet UIImageView *heartImageView;
@property (nonatomic, strong) CAShapeLayer *maskLayer;
@property (nonatomic, strong) CADisplayLink *displayLink;
@property (nonatomic, assign) CGFloat num;
@property (nonatomic, strong) UIImageView *topEmptyImageView;
@property (nonatomic, assign) NSInteger currentRelationValue;
@property (nonatomic, assign) NSInteger maxRelationValue;
@property (nonatomic, assign) CGFloat redHeartZoneHeight;
@property (nonatomic, assign) BOOL isHasShow;
@end
@implementation ViewController
#pragma mark - Override Base Function
- (void)viewDidLoad
{
[super viewDidLoad];
}
- (void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event
{
[self doHeartAnimationWithCurrentValue:20 maxValue:100];
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(2 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
[self doHeartAnimationWithCurrentValue:50 maxValue:100];
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(2 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
[self doHeartAnimationWithCurrentValue:80 maxValue:100];
});
});
}
#pragma mark - Object Private Function
- (void)doHeartAnimationWithCurrentValue:(NSInteger)currentValue maxValue:(NSInteger)maxValue
{
if (currentValue == 0 || maxValue == 0) {
return;
}
self.num = 0.0;
self.currentRelationValue = currentValue;
self.maxRelationValue = maxValue;
self.redHeartZoneHeight = self.currentRelationValue * self.heartImageView.image.size.height / self.maxRelationValue * 1.0;
if (!self.isHasShow) { //第一次展示
//首先放大
self.isHasShow = YES;
[UIView animateWithDuration:0.5 animations:^{
self.heartImageView.transform = CGAffineTransformMakeScale(1.3, 1.3);
} completion:^(BOOL finished) {
[UIView transitionWithView:self.heartImageView duration:0.4 options:UIViewAnimationOptionTransitionFlipFromRight animations:^{
self.heartImageView.image = [UIImage imageNamed:@"relatiion_empty_heart"];
} completion:^(BOOL finished) {
self.heartImageView.transform = CGAffineTransformIdentity;
[self.view addSubview:self.topEmptyImageView];
UIBezierPath *maskPath = [UIBezierPath bezierPathWithRect:CGRectMake(0.0, 0.0, self.heartImageView.bounds.size.width, self.heartImageView.image.size.height)];
self.maskLayer.path = maskPath.CGPath;
self.heartImageView.layer.mask = self.maskLayer;
self.maskLayer = self.maskLayer;
[self doDisplayLinkAnimation];
}];
}];
}
else { //不是第一次展示
[self doDisplayLinkAnimation];
}
}
//做上涨的注入动画
- (void)doDisplayLinkAnimation
{
if (_displayLink) {
[_displayLink invalidate];
_displayLink = nil;
}
[self.displayLink addToRunLoop:[NSRunLoop mainRunLoop] forMode:NSRunLoopCommonModes];
}
#pragma mark - Getter && Setter
//遮罩层
- (CAShapeLayer *)maskLayer
{
if (!_maskLayer) {
_maskLayer = [[CAShapeLayer alloc] init];
}
return _maskLayer;
}
//顶部空心层
- (UIImageView *)topEmptyImageView
{
if (!_topEmptyImageView) {
_topEmptyImageView = [[UIImageView alloc] initWithFrame:self.heartImageView.frame];
_topEmptyImageView.image = [UIImage imageNamed:@"relatiion_empty_heart"];
}
return _topEmptyImageView;
}
//CADisplayLink
- (CADisplayLink *)displayLink
{
if (!_displayLink) {
_displayLink = [CADisplayLink displayLinkWithTarget:self selector:@selector(displayLinkDidRun)];
}
return _displayLink;
}
#pragma mark - Action && Notification
//displayLink回调
- (void)displayLinkDidRun
{
self.num = self.num + 0.1;
self.heartImageView.image = [UIImage imageNamed:@"relation_heart"];
self.maskLayer.path = [UIBezierPath bezierPathWithRect:CGRectMake(0.0, self.heartImageView.image.size.height - self.num, self.heartImageView.bounds.size.width, self.num)].CGPath;
if (self.num > self.redHeartZoneHeight) {
[self.displayLink invalidate];
}
}
@end
具体效果参见上面的示例,那个就是我上面代码运行的结果,可以看见心涨了三次,第一次是0--20
,第二次是0--50
,第三次是0--80
,可以看见每次都是从0开始上涨。这个动画的核心思想就是根据CADisplayLink
不断的移动mask
。
后记
本篇主要讲述了一种不规则形状注入动画的实现,感兴趣的给个赞或者关注~~~