CAShapeLayer
一言以蔽之:CAShapeLayer 可以根据贝塞尔曲线描绘出的路径而生成对应的图形
综合例子
- 效果图
-
关键技术点剖析
-
分析 QQ 粘性动画的关键点就是当手势拖动时候2个圆之间那个形状怎么绘制
答案:将2个圆的某一时刻之间形成的形状用数学抽象来计算。
- 拖动到超过某个范围的时候怎么执行爆炸动画
UIImageView 可以执行帧动画,类似于 Flash 效果
关键代码
- (void)pan:(UIPanGestureRecognizer *)pan{ //当前移动的偏移量 CGPoint transP = [pan translationInView:self]; //改变红点的位置 //transform并没有修改自身的 center(center 是 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;}
完整的代码,