动画示例(十二) —— 一种不规则形状注入动画的实现 (一)

版本记录

版本号 时间
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弹性动画的实现 (一)

功能需求

需要做不规则形状的注入的动画,并且根据不同的百分比涨的高度也不同,就像一个不规则水杯进行注水。

具体动画示例如下所示:

3691932-62aca00377d3ab73.gif

大家可以看见这个动画分为以下几个部分:

  • 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中的视图

3691932-f05ea32444235d08.png

下面就是直接看代码了。

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

后记

本篇主要讲述了一种不规则形状注入动画的实现,感兴趣的给个赞或者关注~~~

3691932-9a4b6f55a8017664.png

猜你喜欢

转载自blog.csdn.net/weixin_34029680/article/details/87637703