Files
knowledge-kit/Chapter1 - iOS/1.26.md
2020-02-25 17:46:51 +08:00

5.8 KiB
Raw Blame History

CAShapeLayer

一言以蔽之CAShapeLayer 可以根据贝塞尔曲线描绘出的路径而生成对应的图形

综合例子

  • 效果图

QQ粘性动画

  • 关键技术点剖析

    • 分析 QQ 粘性动画的关键点就是当手势拖动时候2个圆之间那个形状怎么绘制

      答案将2个圆的某一时刻之间形成的形状用数学抽象来计算。 轨迹分解

    • 拖动到超过某个范围的时候怎么执行爆炸动画

      UIImageView 可以执行帧动画,类似于 Flash 效果

      关键代码

      - (void)pan:(UIPanGestureRecognizer *)pan{
          //当前移动的偏移量
          CGPoint transP = [pan translationInView:self];
          //改变红点的位置
          //transform并没有修改自身的 centercenter 是 layer 的position只是修改了 frame
          NSLog(@"偏移量:%@",NSStringFromCGPoint(transP));
          CGPoint center = self.center;
          center.x += transP.x;
          center.y += transP.y;
          self.center = center;
      
          //self.transform = CGAffineTransformTranslate(self.transform, transP.x, transP.y);
          //手势复位:设置坐标原点位上次的坐标
          [pan setTranslation:CGPointZero inView:self];
      
          CGFloat distance = [self distanceWith:self.smallCircle bigCircle:self];
          NSLog(@"%f",distance);
      
      
          CGFloat smallCircleRadius = self.bounds.size.width * 0.5;
          smallCircleRadius = smallCircleRadius - distance/10;
      
          if (smallCircleRadius < 3) {
              smallCircleRadius = 3;
          }
          self.smallCircle.bounds = CGRectMake(0, 0, smallCircleRadius*2, smallCircleRadius*2);
          self.smallCircle.layer.cornerRadius = smallCircleRadius;
      
          if (self.smallCircle.hidden == NO) {
              //返回一个不规则的路径
              UIBezierPath *path = [self drawTracertWithSmallCircle:self.smallCircle bigCircle:self];
              //将形状转换为一个形状图层
              self.shapeLayer.path = path.CGPath;//根据路径生成形状
          }
          //创建形状图层
          [self.superview.layer insertSublayer:self.shapeLayer atIndex:0];
      
          if (distance > 60) {
              self.smallCircle.hidden = YES;
              [self.shapeLayer removeFromSuperlayer];
          }
      
          if (pan.state == UIGestureRecognizerStateEnded) {
              //结束手势
              if (distance < 60) {
                  [self.shapeLayer removeFromSuperlayer];
                  self.center = self.smallCircle.center;
                  self.smallCircle.hidden = NO;
              }
              else{
                  //手势拖拽超过60则播放一个动画
                  UIImageView *imageView = [[UIImageView alloc] initWithFrame:self.bounds];
      
                  NSMutableArray *images = [NSMutableArray array];
      
                  for (int i=0; i<8; i++) {
                      NSString *imageName = [NSString stringWithFormat:@"%d",i+1];
                      UIImage *image = [UIImage imageNamed:imageName];
                      [images addObject:image];
                  }
                  imageView.animationImages = images;
                  [imageView setAnimationDuration:1];
                  [imageView startAnimating];
                  [self addSubview:imageView];
                  //动画结束移除本身
                  dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(1 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
                      [self removeFromSuperview];
                  });
              }
          }
      
      }
      
      - (CGFloat )distanceWith:(UIView *)smallCircle bigCircle:(UIView *)bigScirle{
          CGFloat offsetX = bigScirle.frame.origin.x - smallCircle.frame.origin.x;
          CGFloat offsetY = bigScirle.frame.origin.y - smallCircle.frame.origin.y;
          return sqrt( pow(offsetX, 2) + pow(offsetY, 2));
      }
      
      //将2个圆运行的变化轨迹用代码模拟
      - (UIBezierPath *)drawTracertWithSmallCircle:(UIView *)smallCircle bigCircle:(UIView *)bigCircle{
      
          CGFloat X1 = smallCircle.center.x;
          CGFloat X2 = bigCircle.center.x;
          CGFloat Y1 = smallCircle.center.y;
          CGFloat Y2 = bigCircle.center.y;
      
          CGFloat r1 = smallCircle.bounds.size.width/2;
          CGFloat r2 = bigCircle.bounds.size.width/2;
      
          CGFloat d = [self distanceWith:smallCircle bigCircle:bigCircle];
          //Ø 代表角度
          CGFloat SinØ = (X2 - X1)/d;
          CGFloat CosØ = (Y2 - Y1)/d;
      
          CGPoint pointA = CGPointMake(X1 - r1*CosØ, Y1 + r1*SinØ);
      
          CGPoint pointB = CGPointMake(X1 + r1*CosØ, Y1 - r1*SinØ);
      
          CGPoint pointC = CGPointMake(X2 + r2*CosØ, Y2 - r2*SinØ);
      
          CGPoint pointD = CGPointMake(X2 - r2*CosØ, Y2 + r2*SinØ);
      
          CGPoint pointO = CGPointMake(X1 + SinØ *d/2, Y1 + CosØ*d/2);
      
          CGPoint pointP = CGPointMake(X1 + SinØ *d/2,Y1 + CosØ*d/2 );
      
          //描述路径
          UIBezierPath *path = [UIBezierPath bezierPath];
      
          //AB
          [path moveToPoint:pointA];
          [path addLineToPoint:pointB];
      
          //BC(曲线)
          [path addQuadCurveToPoint:pointC controlPoint:pointP];
      
          //CD
          [path addLineToPoint:pointD];
      
          //DA(曲线)
          [path addQuadCurveToPoint:pointA controlPoint:pointO];
      
          return path;
      }
      

完整的代码,Github地址