核心动画-点赞动画

129 阅读4分钟

上效果图:

IMG_6670.GIF

上代码

#import <UIKit/UIKit.h>

@interface ZanLikeView : UIView

//点赞前图片
@property (nonatomic, strong) UIImageView *likeBefore;
//点赞后图片
@property (nonatomic, strong) UIImageView *likeAfter;
//点赞时长
@property (nonatomic, assign) CGFloat     likeDuration;
//点赞按钮填充颜色
@property (nonatomic, strong) UIColor     *zanFillColor;

@end

#import "ZanLikeView.h"
#define CCFavoriteViewLikeBeforeTag 1 //点赞
#define CCFavoriteViewLikeAfterTag  2 //取消点赞

@implementation ZanLikeView
- (id)initWithFrame:(CGRect)frame{
    self = [super initWithFrame:frame];
    if(self) {
        
        //为点赞图片之前添加图片以及手势
        _likeBefore = [[UIImageView alloc]initWithFrame:frame];
        _likeBefore.contentMode = UIViewContentModeCenter;
        _likeBefore.image = [UIImage imageNamed:@"icon_home_like_before"];
        _likeBefore.userInteractionEnabled = YES;
        _likeBefore.tag = CCFavoriteViewLikeBeforeTag;
        [_likeBefore addGestureRecognizer:[[UITapGestureRecognizer alloc] initWithTarget:self action:@selector(handleGesture:)]];
        [self addSubview:_likeBefore];
        
        //为点赞图片之后添加图片以及手势
        _likeAfter = [[UIImageView alloc]initWithFrame:frame];
        _likeAfter.contentMode = UIViewContentModeCenter;
        _likeAfter.image = [UIImage imageNamed:@"icon_home_like_after"];
        _likeAfter.userInteractionEnabled = YES;
        _likeAfter.tag = CCFavoriteViewLikeAfterTag;
        [_likeAfter setHidden:YES];
        [_likeAfter addGestureRecognizer:[[UITapGestureRecognizer alloc] initWithTarget:self action:@selector(handleGesture:)]];
        [self addSubview:_likeAfter];
    }
    return self;
}

- (void)handleGesture:(UITapGestureRecognizer *)sender {
    switch (sender.view.tag) {
        case CCFavoriteViewLikeBeforeTag: {
            //开始动画(点赞)
            [self startLikeAnim:YES];
            break;
        }
        case CCFavoriteViewLikeAfterTag: {
            //开始动画(取消点赞)
            [self startLikeAnim:NO];
            break;
        }
    }
}

-(void)startLikeAnim:(BOOL)isLike {
    
    _likeBefore.userInteractionEnabled = NO;
    _likeAfter.userInteractionEnabled = NO;
    
    //判断点赞/取消点赞
    if(isLike) {
        //length:三角形贝塞尔曲线中的length.
        CGFloat length = 30;
        //动画时长:
        CGFloat duration = self.likeDuration > 0? self.likeDuration :0.5f;
        
        //for循环每 30°角创建一个上述的三角形.我们需要创建 6个 就循环6次
        for(int i=0;i<6;i++) {
            //创建shapeLayer
            CAShapeLayer *layer = [[CAShapeLayer alloc]init];
            //指定layer的位置 ,以_likeBefore图片为中心._likeBefore 是我们看到白色的❤️背景视图(UIImageView)
            layer.position = _likeBefore.center;
            //填充颜色,默认为红色
            layer.fillColor = self.zanFillColor == nil?[UIColor redColor].CGColor: self.zanFillColor.CGColor;
            
            //创建起始路径--路径就是一个倒三角形.
            UIBezierPath *startPath = [UIBezierPath bezierPath];
            [startPath moveToPoint:CGPointMake(-2, -length)];
            [startPath addLineToPoint:CGPointMake(2, -length)];
            [startPath addLineToPoint:CGPointMake(0, 0)];
            //将startPath转化为CGPath,添加到layer上.
            layer.path = startPath.CGPath;
            
            //注: 当x,y,z值为0时,代表在该轴方向上不进行旋转,当值为-1时,代表在该轴方向上进行逆时针旋转,当值为1时,代表在该轴方向上进行顺时针旋转
            /*
             注: CATransform3DMakeRotation()函数 当x,y,z值为0时,代表在该轴方向上不进行旋转,当值为-1时,代表在该轴方向上进行逆时针旋转,当值为1时,代表在该轴方向上进行顺时针旋转
             因为我们是需要60°创建一个layer所以需要顺时针 M_PI / 3.0f = 60°. 每循环一次则创建第N个角度乘60°.
             */
            layer.transform = CATransform3DMakeRotation(M_PI / 3.0f * i, 0.0, 0.0, 1.0);
            [self.layer addSublayer:layer];
            
            //创建动画组
            CAAnimationGroup *group = [[CAAnimationGroup alloc] init];
            //是否在播放完成后移除。这是一个非常重要的属性,有的时候我们希望动画播放完成,但是保留最终的播放效果是,这个属性一定要改为NO,否则无效。
            group.removedOnCompletion = NO;
            //动画的节奏
            /*
             所谓节奏是什么什么意思呢?就是动画执行的快慢交替。有如下几个可选项
             kCAMediaTimingFunctionLinear//线性节奏,就是匀速
             kCAMediaTimingFunctionEaseIn//淡入,缓慢加速进入,然后匀速
             kCAMediaTimingFunctionEaseOut//淡出,匀速,然后缓慢减速移除
             kCAMediaTimingFunctionEaseInEaseOut//淡入淡出,结合以上两者
             kCAMediaTimingFunctionDefault//默认效果
             */
            group.timingFunction = [CAMediaTimingFunction functionWithName:kCAMediaTimingFunctionEaseInEaseOut];
            //是播放结束后的状态
            /*
             它有四个枚举值
             kCAFillModeForwards//保持结束时状态
             kCAFillModeBackwards//保持开始时状态
             kCAFillModeBoth//保持两者,我没懂两者是什么概念,实际使用中与kCAFillModeBackwards相同
             kCAFillModeRemoved//移除
             这个属性使用的时候要设置removedOnCompletion = NO,否则你是看不到效果的
             */
            group.fillMode = kCAFillModeForwards;
            //时长
            group.duration = duration;
            
            /*
             CABasicAnimation用来创建基于两个状态的动画,你只需要给出两个状态,一个初始状态一个终止状态,系统自动为你将中间的动画补全
             from就是指定初始状态
             toValue就是终止状态
             by就是状态的增量
             */
            CABasicAnimation *scaleAnim = [CABasicAnimation animationWithKeyPath:@"transform.scale"];
            scaleAnim.fromValue = @(0.0);
            scaleAnim.toValue = @(1.0);
            scaleAnim.duration = duration * 0.2f;
            
            //创建
            UIBezierPath *endPath = [UIBezierPath bezierPath];
            [endPath moveToPoint:CGPointMake(-2, -length)];
            [endPath addLineToPoint:CGPointMake(2, -length)];
            [endPath addLineToPoint:CGPointMake(0, -length)];
            
            /*
             从我们上一个layer的path位置开始向我们结束位置的path过渡,并且注意开始时间
             pathAnim.beginTime是 duration 0.2也就是说 在上一个动画结束的时间点才开始结束过渡,过渡的时长剩余是duration 0.8.这样两个连贯在一起的动画就执行完了,最后把动画加到动画组 天加给layer.
             */
            //结束点路径
            CABasicAnimation *pathAnim = [CABasicAnimation animationWithKeyPath:@"path"];
            pathAnim.fromValue = (__bridge id)layer.path;
            pathAnim.toValue = (__bridge id)endPath.CGPath;
            pathAnim.beginTime = duration * 0.2f;
            pathAnim.duration = duration * 0.8f;
            //给动画组添加2个动画(scaleAnim,pathAnim)
            [group setAnimations:@[scaleAnim, pathAnim]];
            //给图层添加动画组
            [layer addAnimation:group forKey:nil];
        }
        
        //_likeAfter 取消隐藏
        [_likeAfter setHidden:NO];
        //透明度为0.
        _likeAfter.alpha = 0.0f;
        //旋转120,并缩小0.5倍
        _likeAfter.transform = CGAffineTransformScale(CGAffineTransformMakeRotation(-M_PI/3*2), 0.5f, 0.5f);
        //创建基础动画
        /*
         Duration: 时长
         delay:延时
         Damping:弹簧效果时长
         velocity:速度
         animation:动画内容
         completion:动画结束
         */
        [UIView animateWithDuration:0.4f
                              delay:0.2f
             usingSpringWithDamping:0.6f
              initialSpringVelocity:0.8f
                            options:UIViewAnimationOptionCurveEaseIn
                         animations:^{
                             self.likeBefore.alpha = 0.0f;
                             self.likeAfter.alpha = 1.0f;
                             self.likeAfter.transform = CGAffineTransformScale(CGAffineTransformMakeRotation(0), 1.0f, 1.0f);
                         }
                         completion:^(BOOL finished) {
                             self.likeBefore.alpha = 1.0f;
                             self.likeBefore.userInteractionEnabled = YES;
                             self.likeAfter.userInteractionEnabled = YES;
                         }];
    }else {
        //取消点赞
        _likeAfter.alpha = 1.0f;
        _likeAfter.transform = CGAffineTransformScale(CGAffineTransformMakeRotation(0), 1.0f, 1.0f);
        [UIView animateWithDuration:0.35f
                              delay:0.0f
                            options:UIViewAnimationOptionCurveEaseIn
                         animations:^{
                             self.likeAfter.transform = CGAffineTransformScale(CGAffineTransformMakeRotation(-M_PI_4), 0.1f, 0.1f);
                         }
                         completion:^(BOOL finished) {
                             [self.likeAfter setHidden:YES];
                             self.likeBefore.userInteractionEnabled = YES;
                             self.likeAfter.userInteractionEnabled = YES;
                         }];
    }
}