iOS仿抖音—加载点赞动画效果

iOS仿抖音短视频

iOS仿抖音—左右滑动切换效果
iOS仿抖音—上下滑动播放视频
iOS仿抖音—评论视图滑动消失
iOS仿抖音—加载点赞动画效果
iOS仿抖音—播放视图滑动隐藏

前言

前段时间比较忙,最近终于有时间就继续对仿抖音的demo进行更新,本次更新的主要是抖音上的几种动画,下面先来看下效果图:


抖音

说明

经过观察发现抖音主要要以下几种动画效果:
1、数据加载动画(两个小球来回切换)
2、视频加载动画(直线向两边扩散)
3、红心点赞动画(红心由小变大并向四周扩散)
4、双击点赞动画(多个红心由小变大并逐渐消失)
于是在经过各种资料查找及自我实践中完成了这四种动画,下面就这几种动画做一下简单说明

1、数据加载动画

数据加载

这个动画大致观察发现是一个红球一个绿球左右来回切换实现的,但仔细观察你会发现,在左右切换的过程中有个黑色小球在不断变大缩小,跟随最上面的球运动。
因此我们需要添加三个小球,绿球、红球、黑球,默认绿球在左红球在右,黑球在绿球上

    self.containerView = [[UIView alloc] init];
    self.containerView.center = self.center;
    self.containerView.bounds = CGRectMake(0, 0, 2.1f * kBallWidth, 2.0f * kBallWidth);
    [self addSubview:self.containerView];
    
    // 绿球
    self.greenBall = [[UIView alloc] initWithFrame:CGRectMake(0, 0, kBallWidth, kBallWidth)];
    self.greenBall.center = CGPointMake(kBallWidth * 0.5f, self.containerView.bounds.size.height * 0.5f);
    self.greenBall.layer.cornerRadius = kBallWidth * 0.5f;
    self.greenBall.layer.masksToBounds = YES;
    self.greenBall.backgroundColor = GKColorRGB(35, 246, 235);
    [self.containerView addSubview:self.greenBall];
    
    // 红球
    self.redBall = [[UIView alloc] initWithFrame:CGRectMake(0, 0, kBallWidth, kBallWidth)];
    self.redBall.center = CGPointMake(self.containerView.bounds.size.width - kBallWidth * 0.5f, self.containerView.bounds.size.height * 0.5f);
    self.redBall.layer.cornerRadius = kBallWidth * 0.5f;
    self.redBall.layer.masksToBounds = YES;
    self.redBall.backgroundColor = GKColorRGB(255, 46, 86);
    [self.containerView addSubview:self.redBall];
    
    // 黑球
    // 第一次动画是正向,绿球在上,红球在下,阴影显示在绿球上
    self.blackBall = [[UIView alloc] initWithFrame:CGRectMake(0, 0, kBallWidth, kBallWidth)];
    self.blackBall.backgroundColor = GKColorRGB(12, 11, 17);
    self.blackBall.layer.cornerRadius = kBallWidth * 0.5f;
    self.blackBall.layer.masksToBounds = YES;
    [self.greenBall addSubview:self.blackBall];

开始动画,绿球向右放大运动,红球向左缩小运动,绿球到最右边后,红球向右放大运动,绿球向左缩小运动,完成一次循环,黑球是绿球和红球的重合部分,主要代码如下:

- (void)updateBallAnimations {
    if (self.moveDirection == GKBallMoveDirectionPositive) { // 正向
        CGPoint center = self.greenBall.center;
        center.x += kBallSpeed;
        self.greenBall.center = center;
        
        center = self.redBall.center;
        center.x -= kBallSpeed;
        self.redBall.center = center;
        
        // 缩放动画,绿球放大,红球缩小
        self.greenBall.transform = [self ballLargerTransformOfCenterX:center.x];
        self.redBall.transform   = [self ballSmallerTransformOfCenterX:center.x];
        
        // 更新黑球位置
        CGRect blackBallFrame = [self.redBall convertRect:self.redBall.bounds toCoordinateSpace:self.greenBall];
        self.blackBall.frame = blackBallFrame;
        self.blackBall.layer.cornerRadius = self.blackBall.bounds.size.width * 0.5f;
        
        // 更新方向 改变三个球的相对位置
        if (CGRectGetMaxX(self.greenBall.frame) >= self.containerView.bounds.size.width || CGRectGetMinX(self.redBall.frame) <= 0) {
            // 切换为反向
            self.moveDirection = GKBallMoveDirectionNegative;
            
            // 反向运动时,红球在上,绿球在下
            [self.containerView bringSubviewToFront:self.redBall];
            
            // 黑球放在红球上面
            [self.redBall addSubview:self.blackBall];
            
            // 重置动画
            [self resetAnimation];
        }
    }else if (self.moveDirection == GKBallMoveDirectionNegative) { // 反向
        // 更新绿球位置
        CGPoint center = self.greenBall.center;
        center.x -= kBallSpeed;
        self.greenBall.center = center;
        
        // 更新红球位置
        center = self.redBall.center;
        center.x += kBallSpeed;
        self.redBall.center = center;
        
        // 缩放动画 红球放大 绿球缩小
        self.redBall.transform = [self ballLargerTransformOfCenterX:center.x];
        self.greenBall.transform = [self ballSmallerTransformOfCenterX:center.x];
        
        // 更新黑球位置
        CGRect blackBallFrame = [self.greenBall convertRect:self.greenBall.bounds toCoordinateSpace:self.redBall];
        self.blackBall.frame = blackBallFrame;
        self.blackBall.layer.cornerRadius = self.blackBall.bounds.size.width * 0.5f;
        
        // 更新方向 改变三个球的相对位置
        if (CGRectGetMinX(self.greenBall.frame) <= 0 || CGRectGetMaxX(self.redBall.frame) >= self.containerView.bounds.size.width) {
            // 切换为正向
            self.moveDirection = GKBallMoveDirectionPositive;
            // 正向运动 绿球在上 红球在下
            [self.containerView bringSubviewToFront:self.greenBall];
            // 黑球放在绿球上面
            [self.greenBall addSubview:self.blackBall];
            // 重置动画
            [self resetAnimation];
        }
    }
}

具体代码可在demo中的GKBallLoadingView中查看。

2、视频加载动画

视频加载

这个动画比较简单,首先以x轴为中心进行缩放动画,然后再修改的透明度即可,主要代码如下:

    // 创建动画组
    CAAnimationGroup *animationGroup = [CAAnimationGroup animation];
    animationGroup.duration = GKLineLoadingDuration;
    animationGroup.beginTime = CACurrentMediaTime();
    animationGroup.repeatCount = MAXFLOAT;
    animationGroup.timingFunction = [CAMediaTimingFunction functionWithName:kCAMediaTimingFunctionEaseInEaseOut];
    
    // x轴缩放动画(transform.scale是以view的中心点为中心开始缩放的)
    CABasicAnimation *scaleAnimation = [CABasicAnimation animation];
    scaleAnimation.keyPath = @"transform.scale.x";
    scaleAnimation.fromValue = @(1.0f);
    scaleAnimation.toValue = @(1.0f * self.superview.frame.size.width);
    
    // 透明度渐变动画
    CABasicAnimation *alphaAnimation = [CABasicAnimation animation];
    alphaAnimation.keyPath = @"opacity";
    alphaAnimation.fromValue = @(1.0f);
    alphaAnimation.toValue = @(0.5f);
    
    animationGroup.animations = @[scaleAnimation, alphaAnimation];
    // 添加动画
    [self.layer addAnimation:animationGroup forKey:nil];

具体代码可在demo中的GKLineLoadingView中查看。

3、红心点赞动画

红心点赞

这个动画的主要是通过CAShapeLayer和贝赛尔曲线绘制三角形,循环创建6次在6个方向绘制三角形,并加入动画,然后进行红心的缩放动画,主要代码如下:

    if (isLike) {
        CGFloat length      = 30;
        CGFloat duration    = 0.5f;
        for (NSInteger i = 0; i < 6; i++) {
            CAShapeLayer *layer = [CAShapeLayer layer];
            layer.position = self.likeBeforeImgView.center;
            layer.fillColor = GKColorRGB(232, 50, 85).CGColor;

            UIBezierPath *startPath = [UIBezierPath bezierPath];
            [startPath moveToPoint:CGPointMake(-2, -length)];
            [startPath addLineToPoint:CGPointMake(2, -length)];
            [startPath addLineToPoint:CGPointMake(0, 0)];
            layer.path = startPath.CGPath;

            layer.transform = CATransform3DMakeRotation(M_PI / 3.0f * i, 0, 0, 1.0);
            [self.layer addSublayer:layer];

            CAAnimationGroup *group = [CAAnimationGroup animation];
            group.removedOnCompletion = NO;
            group.timingFunction = [CAMediaTimingFunction functionWithName:kCAMediaTimingFunctionEaseInEaseOut];
            group.fillMode = kCAFillModeForwards;
            group.duration = duration;

            CABasicAnimation *scaleAnim = [CABasicAnimation animationWithKeyPath:@"transform.scale"];
            scaleAnim.fromValue = @(0.0f);
            scaleAnim.toValue = @(1.0f);
            scaleAnim.duration = duration * 0.2f;

            UIBezierPath *endPath = [UIBezierPath bezierPath];
            [endPath moveToPoint:CGPointMake(-2, -length)];
            [endPath addLineToPoint:CGPointMake(2, -length)];
            [endPath addLineToPoint:CGPointMake(0, -length)];

            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;

            [group setAnimations:@[scaleAnim, pathAnim]];
            [layer addAnimation:group forKey:nil];
        }
        self.likeAfterImgView.hidden = NO;
        self.likeAfterImgView.alpha = 0.0f;
        
        self.likeAfterImgView.transform = CGAffineTransformMakeScale(0.1f, 0.1f);
        
        [UIView animateWithDuration:0.15 animations:^{
            self.likeAfterImgView.transform = CGAffineTransformMakeScale(1.0f, 1.0f);
            self.likeAfterImgView.alpha = 1.0f;
            self.likeBeforeImgView.alpha = 0.0f;
        } completion:^(BOOL finished) {
            self.likeAfterImgView.transform = CGAffineTransformIdentity;
            self.likeBeforeImgView.alpha = 1.0f;
        }];
    }else {
        self.likeAfterImgView.alpha = 1.0f;
        self.likeAfterImgView.transform = CGAffineTransformMakeScale(1.0f, 1.0f);
        [UIView animateWithDuration:0.15 animations:^{
            self.likeAfterImgView.transform = CGAffineTransformMakeScale(0.3f, 0.3f);
        } completion:^(BOOL finished) {
            self.likeAfterImgView.transform = CGAffineTransformIdentity;
            self.likeAfterImgView.hidden = YES;
        }];
    }

具体代码可在demo中的GKLikeView中查看

4、双击点赞动画

双击点赞

这个动画主要是通过在- (void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event方法中根据触摸的位置不停创建红心,并放大、透明,然后销毁,主要代码如下:

- (void)createAnimationWithTouch:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event {
    UITouch *touch = [touches anyObject];
    if (touch.tapCount <= 1.0f) return;
    
    CGPoint point = [touch locationInView:touch.view];
    UIImage *image = [UIImage imageNamed:@"likeHeart"];
    UIImageView *imgView = [[UIImageView alloc] initWithFrame:CGRectMake(0, 0, ADAPTATIONRATIO * 160.0f, ADAPTATIONRATIO * 160.0f)];
    imgView.image = image;
    imgView.contentMode = UIViewContentModeScaleAspectFill;
    imgView.center = point;
    
    // 随机左右显示
    int leftOrRight = arc4random() % 2;
    leftOrRight = leftOrRight ? leftOrRight : -1;
    imgView.transform = CGAffineTransformRotate(imgView.transform, M_PI / 9.0f * leftOrRight);
    [touch.view addSubview:imgView];
    
    // 出现的时候回弹一下
    __block UIImageView *blockImgV = imgView;
    __block UIImage *blockImage = image;
    
    [UIView animateWithDuration:0.1 animations:^{
        blockImgV.transform = CGAffineTransformScale(blockImgV.transform, 1.2f, 1.2f);
    } completion:^(BOOL finished) {
        blockImgV.transform = CGAffineTransformScale(blockImgV.transform, 0.8f, 0.8f);
        
        // 向上飘,放大,透明
        [self performSelector:@selector(animationToTop:) withObject:@[blockImgV, blockImage] afterDelay:0.3f];
    }];
}

具体代码可在demo中的GKDoubleLikeView中查看。

最后

上面提到的所有动画都可以在github上的demoGKDYVideo中查看,如果觉得不错,还请来个star!

赞赏

您的赞赏是对我最大的支持

微信赞赏
支付宝赞赏

参考

抖音点赞动画实现—iOS
iOS开发_仿抖音点赞动画功能的实现

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 159,716评论 4 364
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 67,558评论 1 294
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 109,431评论 0 244
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 44,127评论 0 209
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 52,511评论 3 287
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 40,692评论 1 222
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 31,915评论 2 313
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 30,664评论 0 202
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 34,412评论 1 246
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 30,616评论 2 245
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 32,105评论 1 260
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 28,424评论 2 254
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 33,098评论 3 238
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 26,096评论 0 8
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 26,869评论 0 197
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 35,748评论 2 276
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 35,641评论 2 271

推荐阅读更多精彩内容

  • 前言 今天给大家分享一下抖音的点赞动画的实现, 废话不多说上图 本篇文章主要包含技术点: CAShapeLayer...
    iOS猿_员阅读 13,572评论 7 49
  • text-align: center的作用是什么,作用在什么元素上?能让什么元素水平居中text-align属性通...
    YangJeremy阅读 606评论 0 51
  • 写作第二天 起床:6点钟起床 天气:晴 心情:兴奋 任务清单 周目标·完成进度 学习·信息·阅读 健康·饮食·锻炼...
    爱生活_3b83阅读 174评论 0 0