今天看啥  ›  专栏  ›  清风烈酒2157

核心动画(5)之抖音专辑音符实现

清风烈酒2157  · 简书  ·  · 2020-12-24 18:24

前言

利用核心动画实现抖音专辑音符播放功能

image.png

实现

设计思路

  1. 黑色专辑背景图
  2. 头像
  3. 三个不同的音符旋转

专辑背景容器视图

以下实现在 initWithFrame

  1. 初始化 layer 数据,方便删除情况动画和 layer
 self.noteLayers = [NSMutableArray array];
  1. 专辑背景容器视图
self.albumContainer =[[UIView alloc]initWithFrame:self.bounds];
[self addSubview:self.albumContainer];
  1. 添加唱片的背景图
CALayer *backgroudLayer = [CALayer layer];
        backgroudLayer.frame = self.bounds;
        backgroudLayer.contents = (id)[UIImage imageNamed:@"music_cover"].CGImage;
        [self.albumContainer.layer addSublayer:backgroudLayer];
  1. 放在唱片的内部图片

图片要切圆角

CGFloat w = CGRectGetWidth(frame) / 2.0f;
        CGFloat h = CGRectGetHeight(frame) / 2.0f;
        CGRect albumFrame = CGRectMake(w / 2.0f, h / 2.0f, w, h);
        self.album = [[UIImageView alloc]initWithFrame:albumFrame];
        self.album.contentMode = UIViewContentModeScaleAspectFill;
        [self.albumContainer addSubview:self.album];
        
        self.album.layer.cornerRadius = h / 2.0f;
        self.album.layer.masksToBounds = YES;

开始动画startAnimation

  1. 清除所有layer的动画
[self.noteLayers enumerateObjectsUsingBlock:^(CALayer * noteLayer, NSUInteger idx, BOOL *stop) {
        [noteLayer removeFromSuperlayer];
    }];
    [self.layer removeAllAnimations];
  1. 音符动画
 [self addNotoAnimation:@"icon_home_musicnote1" delayTime:0.0f rate:rate];
    [self addNotoAnimation:@"icon_home_musicnote2" delayTime:1.0f rate:rate];
    [self addNotoAnimation:@"icon_home_musicnote1" delayTime:2.0f rate:rate];
  1. 专辑动画,头像
CABasicAnimation* rotationAnimation;
    rotationAnimation = [CABasicAnimation animationWithKeyPath:@"transform.rotation.z"];
    rotationAnimation.toValue = [NSNumber numberWithFloat: M_PI * 2.0];
    rotationAnimation.duration = 6.0f;
    rotationAnimation.cumulative = YES;
    rotationAnimation.repeatCount = MAXFLOAT;
    [self.albumContainer.layer addAnimation:rotationAnimation forKey:@"rotationAnimation"];

addNotoAnimation

  1. 创建一个动画组 设置一些基本参数
CAAnimationGroup *animationGroup = [[CAAnimationGroup alloc]init];
    animationGroup.duration = rate/4.0f;
    animationGroup.beginTime = CACurrentMediaTime() + delayTime;
    animationGroup.repeatCount = MAXFLOAT;
    animationGroup.removedOnCompletion = NO;
    animationGroup.fillMode = kCAFillModeForwards;
    animationGroup.timingFunction = [CAMediaTimingFunction functionWithName:kCAMediaTimingFunctionLinear];
    
  1. 创建音符的贝塞尔路径
//X轴左右侧偏移量(可修改,目的调整与背景图直接的间隙)
    CGFloat sideXLength = 40.0f;
    //Y轴上下偏移量(可修改,目的调整与背景图直接的间隙)
    CGFloat sideYLength = 100.0f;
    
    //贝赛尔曲线开始点
    //beginPoint 开始点: 当前视图X坐标中心 向 左偏移 5dp (X轴是左右) Y的坐标是当前视图高度 就是最下面.
    CGPoint beginPoint = CGPointMake(CGRectGetMidX(self.bounds) - 5, CGRectGetMaxY(self.bounds));
    
    //贝塞尔曲线结束点
    //endPoint 结束点: 开始点的X 减去 40左侧偏移(就是距离左侧更远) Y也是 减去偏移之后 到了 视图的外部 左上方.
    CGPoint endPoint = CGPointMake(beginPoint.x - sideXLength, beginPoint.y - sideYLength);
    
    //贝塞尔曲线控制点长度
    NSInteger controlLength = 60;
    //贝塞尔曲线控制点
    //controlPoint 控制点: 开始点 比如 X是 30 - 60/2.0 - 60 = -60,显然已经跑到最左边了 超出了视图范围, Y 后面是+ controlLength 说明是加大 Y坐标.
    CGPoint controlPoint = CGPointMake(beginPoint.x - sideXLength/2.0f - controlLength, beginPoint.y - sideYLength/2.0f + controlLength);
    
    //创建贝塞尔轨迹
    UIBezierPath *customPath = [UIBezierPath bezierPath];
    [customPath moveToPoint:beginPoint];
    //核心代码 二次曲线方程式 可以google查一下
    [customPath addQuadCurveToPoint:endPoint controlPoint:controlPoint];
  1. 添加贝塞尔路径帧动画

CAKeyframeAnimation * pathAnimation = [CAKeyframeAnimation animationWithKeyPath:@"position"];
 pathAnimation.path = customPath.CGPath;
  1. 音符自身旋转动画
CAKeyframeAnimation * rotationAnimation = [CAKeyframeAnimation animationWithKeyPath:@"transform.rotation"];
    //这里实际上是控制动画开始弧度和结束弧度 M_PI(180°) 就是半圆 * 0.10 或者 * -0.10j是为了关键点上下偏移的18°的间隙
    [rotationAnimation setValues:@[
                                   [NSNumber numberWithFloat:0],
                                   [NSNumber numberWithFloat:M_PI * 0.10],
                                   [NSNumber numberWithFloat:M_PI * -0.10]]];
  1. 音符自身透明度帧动画
CAKeyframeAnimation * opacityAnimation = [CAKeyframeAnimation animationWithKeyPath:@"opacity"];
    [opacityAnimation setValues:@[
                                  [NSNumber numberWithFloat:0],
                                  [NSNumber numberWithFloat:0.2f],
                                  [NSNumber numberWithFloat:0.7f],
                                  [NSNumber numberWithFloat:0.2f],
                                  [NSNumber numberWithFloat:0]]];
  1. 音符自身缩放动画
CABasicAnimation *scaleAnimation = [CABasicAnimation animation];
    scaleAnimation.keyPath = @"transform.scale";
    scaleAnimation.fromValue = @(1.0f);
    scaleAnimation.toValue = @(2.0f);
  1. 动画加入动画组
[animationGroup setAnimations:@[pathAnimation, scaleAnimation,  rotationAnimation,opacityAnimation]];
  1. 增加音符layer,并设置图片
CAShapeLayer *layer = [CAShapeLayer layer];
    layer.opacity = 0.0f;
    layer.contents = (__bridge id _Nullable)([UIImage imageNamed:imageName].CGImage);
    layer.frame = CGRectMake(beginPoint.x, beginPoint.y, 10, 10);
   
    [self.layer addSublayer:layer];
  1. 开始执行动画
   [self.noteLayers addObject:layer];
    [layer addAnimation:animationGroup forKey:nil];



原文地址:访问原文地址
快照地址: 访问文章快照