CCAnimationBtn.m 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284
  1. //
  2. // CCAnimationBtn.m
  3. // AnimationButton
  4. //
  5. // Created by sischen on 2017/11/25.
  6. // Copyright © 2017年 pcbdoor.com. All rights reserved.
  7. //
  8. #import "CCAnimationBtn.h"
  9. #define RGB_COLOR(_STR_,a) ([UIColor colorWithRed:[[NSString stringWithFormat:@"%lu", strtoul([[_STR_ substringWithRange:NSMakeRange(1, 2)] UTF8String], 0, 16)] intValue] / 255.0 green:[[NSString stringWithFormat:@"%lu", strtoul([[_STR_ substringWithRange:NSMakeRange(3, 2)] UTF8String], 0, 16)] intValue] / 255.0 blue:[[NSString stringWithFormat:@"%lu", strtoul([[_STR_ substringWithRange:NSMakeRange(5, 2)] UTF8String], 0, 16)] intValue] / 255.0 alpha:a])
  10. @interface CCAnimationBtn ()
  11. @property (nonatomic, strong) NSArray <CAShapeLayer *> *lines;
  12. @property (nonatomic, strong) CALayer *imgLayer;
  13. @property (nonatomic, strong) CALayer *unChosenImgLayer;
  14. @end
  15. @implementation CCAnimationBtn
  16. #pragma mark -
  17. #pragma mark - init
  18. - (instancetype)initWithFrame:(CGRect)frame {
  19. if (self = [super initWithFrame:frame]) {
  20. [self setupUI];
  21. }
  22. return self;
  23. }
  24. -(instancetype)initWithCoder:(NSCoder *)aDecoder{
  25. if (self = [super initWithCoder:aDecoder]) {
  26. [self setupUI];
  27. }
  28. return self;
  29. }
  30. - (void)setupUI{
  31. self.lineCount = 6;
  32. self.lineWidth = 5.5;//11.5;
  33. self.lineLengthPercent = 0.75;
  34. self.imgSizePercent = 0.87;//0.7;
  35. self.animationTime = 1.1;
  36. self.lineColor = RGB_COLOR(@"#ea377f", 1);//[UIColor colorWithRed:0.9686 green:0.2863 blue:0.4471 alpha:1];
  37. self.layer.masksToBounds = YES;
  38. // self.layer.borderColor = [UIColor grayColor].CGColor;
  39. // self.layer.borderWidth = 2;
  40. [self.layer addSublayer:self.unChosenImgLayer];
  41. }
  42. #pragma mark -
  43. #pragma mark - make Animations
  44. -(void)makeChosenAnimation{
  45. self.userInteractionEnabled = NO;
  46. [CATransaction setCompletionBlock:^{
  47. self.userInteractionEnabled = YES;
  48. for (CAShapeLayer *line in self.lines) { [line removeFromSuperlayer]; }
  49. }];
  50. [CATransaction begin];
  51. [self makeAnimationWithUnChosenImageFrom:@1 To:@0];
  52. [self makeComingAnimationWithCenterImage];
  53. for (CAShapeLayer *line in self.lines) {
  54. [self makeAnimationWithLine:line];
  55. }
  56. [CATransaction commit];
  57. }
  58. -(void)makeUnchosenAnimation{
  59. self.userInteractionEnabled = NO;
  60. [CATransaction setCompletionBlock:^{
  61. self.userInteractionEnabled = YES;
  62. }];
  63. [CATransaction begin];
  64. [self makeAnimationWithUnChosenImageFrom:@0 To:@1];
  65. [self makeFadingAnimationWithCenterImage];
  66. [CATransaction commit];
  67. }
  68. -(void)makeAnimationWithUnChosenImageFrom:(id)from To:(id)to {
  69. //透明度动画
  70. CABasicAnimation *anim_opacity = [CABasicAnimation animationWithKeyPath:@"opacity"];
  71. anim_opacity.fillMode = kCAFillModeForwards;
  72. anim_opacity.removedOnCompletion = NO;
  73. anim_opacity.duration = self.animationTime/4.0;
  74. anim_opacity.timingFunction = [CAMediaTimingFunction functionWithName: kCAMediaTimingFunctionEaseIn];
  75. anim_opacity.fromValue = from;
  76. anim_opacity.toValue = to;
  77. [self.unChosenImgLayer addAnimation:anim_opacity forKey:@"opacity"];
  78. }
  79. -(void)makeFadingAnimationWithCenterImage {
  80. CABasicAnimation *anim_scale = [CABasicAnimation animationWithKeyPath:@"transform.scale"];
  81. anim_scale.duration = self.animationTime/4.0;
  82. anim_scale.timingFunction = [CAMediaTimingFunction functionWithName: kCAMediaTimingFunctionDefault];
  83. anim_scale.fromValue = @1;
  84. anim_scale.toValue = @0;
  85. [self.imgLayer addAnimation:anim_scale forKey:@"scale"];
  86. }
  87. -(void)makeComingAnimationWithCenterImage {
  88. [self.layer addSublayer:self.imgLayer];
  89. self.imgLayer.affineTransform = CGAffineTransformScale(self.imgLayer.affineTransform, 0.0, 0.0);
  90. self.imgLayer.affineTransform = CGAffineTransformRotate(self.imgLayer.affineTransform, 0.1 * M_PI);
  91. NSNumber *from = [NSNumber numberWithFloat:0.1 * M_PI];
  92. NSNumber *to = [NSNumber numberWithFloat:0.0];
  93. [self.imgLayer addAnimation:[self imgSpringAnimationWithKeyPath:@"transform.rotation.z" From:from To:to BeginTime:self.animationTime/3.5] forKey:@"rotation"];
  94. [self.imgLayer addAnimation:[self imgSpringAnimationWithKeyPath:@"transform.scale" From:@0.1 To:@1.0 BeginTime:self.animationTime/4.0] forKey:@"scale"];
  95. }
  96. -(void)makeAnimationWithLine:(CAShapeLayer *)line {
  97. [self.layer addSublayer: line];
  98. //缩放动画
  99. CABasicAnimation *anim_scale = [CABasicAnimation animationWithKeyPath:@"transform.scale"];
  100. anim_scale.duration = self.animationTime/4.0;
  101. anim_scale.timingFunction = [CAMediaTimingFunction functionWithName: kCAMediaTimingFunctionEaseIn];
  102. anim_scale.fromValue = @0.01;
  103. anim_scale.toValue = @1.00;
  104. [line addAnimation:anim_scale forKey:@"scale"];
  105. //位移动画
  106. NSInteger index = [self.lines indexOfObject:line];
  107. CGFloat maxSize = MAX(CGRectGetWidth(self.bounds), CGRectGetHeight(self.bounds));
  108. double moveLength = MAX((maxSize * 0.5)/sin(2 * M_PI/self.lineCount), (maxSize * 0.5)/cos(M_PI/self.lineCount));
  109. double xRange = moveLength * sin((2 * M_PI/self.lineCount) *(index + 1));
  110. double yRange = moveLength * cos((2 * M_PI/self.lineCount) *(index + 1));
  111. CABasicAnimation *anim_trans = [CABasicAnimation animationWithKeyPath:@"transform.translation"];
  112. anim_trans.fillMode = kCAFillModeForwards;
  113. anim_trans.removedOnCompletion = NO;
  114. anim_trans.beginTime = CACurrentMediaTime() + self.animationTime/4.0;
  115. anim_trans.duration = self.animationTime/4.0;
  116. anim_trans.timingFunction = [CAMediaTimingFunction functionWithName: kCAMediaTimingFunctionEaseOut];
  117. anim_trans.fromValue = [NSValue valueWithCGPoint:CGPointZero];
  118. anim_trans.toValue = [NSValue valueWithCGPoint:CGPointMake(xRange, -yRange)];
  119. [line addAnimation:anim_trans forKey:@"translation"];
  120. }
  121. #pragma mark -
  122. #pragma mark - Private
  123. -(CASpringAnimation *)imgSpringAnimationWithKeyPath:(NSString *)keypath From:(id)from To:(id)to BeginTime:(CFTimeInterval)beginTime{
  124. CASpringAnimation *anim_spring = [CASpringAnimation animationWithKeyPath:keypath];
  125. anim_spring.fillMode = kCAFillModeForwards;
  126. anim_spring.removedOnCompletion = NO;
  127. anim_spring.fromValue = from;
  128. anim_spring.toValue = to;
  129. anim_spring.beginTime = CACurrentMediaTime() + beginTime;
  130. anim_spring.duration = self.animationTime/2.0;
  131. anim_spring.speed = 1.0/self.animationTime;
  132. anim_spring.mass = 1;
  133. anim_spring.damping = 8;
  134. anim_spring.stiffness = 180;
  135. anim_spring.initialVelocity = 8;
  136. return anim_spring;
  137. }
  138. +(CALayer *)heartLayerWithFrame:(CGRect)rect Fill_S_C:(UIColor *)sc Fill_Q_C:(UIColor *)qc{
  139. CAGradientLayer *visibleLayer = [CAGradientLayer layer];
  140. visibleLayer.frame = rect;
  141. //设置渐变色
  142. visibleLayer.startPoint = CGPointMake(0.5, 0.0);
  143. visibleLayer.endPoint = CGPointMake(0.5, 1.0);
  144. visibleLayer.colors = @[(__bridge id)qc.CGColor,(__bridge id)sc.CGColor];
  145. visibleLayer.backgroundColor = sc.CGColor;
  146. //注意,此心形形状是按照顶部凹角90°来绘制的,需要调整可自行修改
  147. // 左右边距
  148. CGFloat padding = 4.0;
  149. // 小圆半径
  150. CGFloat curveRadius = ((rect.size.width - 2 * padding)/2.0) / (cos(2 * M_PI/8.0) + 1.0);
  151. UIBezierPath *heartPath = [UIBezierPath bezierPath];
  152. // 起点(底部,圆的第一个点)
  153. CGPoint tipLocation = CGPointMake(rect.size.width/2, rect.size.height-padding);
  154. [heartPath moveToPoint:tipLocation];
  155. // (左圆的第二个点)
  156. CGPoint topLeftCurveStart = CGPointMake(padding, rect.size.height/2.8);
  157. // 添加二次曲线
  158. [heartPath addQuadCurveToPoint:topLeftCurveStart controlPoint:CGPointMake(topLeftCurveStart.x, topLeftCurveStart.y + curveRadius)];
  159. // 画左圆
  160. [heartPath addArcWithCenter:CGPointMake(topLeftCurveStart.x+curveRadius, topLeftCurveStart.y) radius:curveRadius startAngle:M_PI endAngle:-M_PI*0.25 clockwise:YES];
  161. // (右圆的第二个点)
  162. CGPoint topRightCurveStart = CGPointMake((rect.size.width - padding) - curveRadius, topLeftCurveStart.y);
  163. // 画右圆
  164. [heartPath addArcWithCenter:CGPointMake(topRightCurveStart.x, topRightCurveStart.y) radius:curveRadius startAngle:-M_PI*0.75 endAngle:0 clockwise:YES];
  165. // 右侧控制点
  166. CGPoint topRightCurveEnd = CGPointMake((rect.size.width - padding), topLeftCurveStart.y);
  167. // 添加二次曲线
  168. [heartPath addQuadCurveToPoint:tipLocation controlPoint:CGPointMake(topRightCurveEnd.x, topRightCurveEnd.y+curveRadius)];
  169. // 设置填充色
  170. [sc setFill];
  171. [heartPath fill];
  172. //轮廓蒙版
  173. CAShapeLayer *shapeMask = [CAShapeLayer layer];
  174. shapeMask.path = heartPath.CGPath;
  175. shapeMask.bounds = rect;
  176. shapeMask.position = visibleLayer.position;
  177. shapeMask.fillColor = [UIColor whiteColor].CGColor;
  178. shapeMask.fillRule = kCAFillRuleEvenOdd;
  179. visibleLayer.mask = shapeMask;
  180. return visibleLayer;
  181. }
  182. -(CGRect)imageLayerFrame{
  183. CGFloat minlength = MIN(self.frame.size.width, self.frame.size.height);
  184. return CGRectMake(0.5*(self.frame.size.width - minlength * self.imgSizePercent),
  185. 0.5*(self.frame.size.height - minlength * self.imgSizePercent),
  186. minlength * self.imgSizePercent,
  187. minlength * self.imgSizePercent);
  188. }
  189. #pragma mark -
  190. #pragma mark - getter/setter
  191. -(CALayer *)imgLayer{
  192. if (!_imgLayer) {
  193. _imgLayer = [self.class heartLayerWithFrame:[self imageLayerFrame] Fill_S_C:self.lineColor Fill_Q_C:RGB_COLOR(@"#ff6fa9", 1)];
  194. }
  195. return _imgLayer;
  196. }
  197. -(CALayer *)unChosenImgLayer{
  198. if (!_unChosenImgLayer) {
  199. _unChosenImgLayer = [self.class heartLayerWithFrame:[self imageLayerFrame] Fill_S_C:[UIColor whiteColor] Fill_Q_C:[UIColor whiteColor]];
  200. }
  201. return _unChosenImgLayer;
  202. }
  203. -(NSArray<CAShapeLayer *> *)lines{
  204. if (!_lines) {
  205. CGRect lineFrame = CGRectMake(0, 0, self.bounds.size.width * self.lineLengthPercent, self.bounds.size.height * self.lineLengthPercent);
  206. NSMutableArray *tmpArr = [[NSMutableArray alloc] init];
  207. for (int i=0; i<self.lineCount; i++) {
  208. CAShapeLayer *lineLayer = [CAShapeLayer layer];
  209. lineLayer.bounds = self.frame;
  210. lineLayer.position = CGPointMake(CGRectGetMidX(self.bounds), CGRectGetMidY(self.bounds));
  211. lineLayer.backgroundColor = self.lineColor.CGColor;
  212. CAShapeLayer *lineMask = [CAShapeLayer layer];
  213. CGMutablePathRef shapePath = CGPathCreateMutable();
  214. CGPathMoveToPoint(shapePath, nil, CGRectGetMidX(lineFrame), CGRectGetMidY(lineFrame));
  215. CGPathAddLineToPoint(shapePath, nil, lineFrame.origin.x + lineFrame.size.width / 2 + 0.5*self.lineWidth, lineFrame.origin.y);
  216. CGPathAddLineToPoint(shapePath, nil, lineFrame.origin.x + lineFrame.size.width / 2 - 0.5*self.lineWidth, lineFrame.origin.y);
  217. CGPathAddLineToPoint(shapePath, nil, CGRectGetMidX(lineFrame), CGRectGetMidY(lineFrame));
  218. lineMask.path = shapePath;
  219. CGPathRelease(shapePath);
  220. lineMask.bounds = lineFrame;
  221. lineMask.position = self.center;
  222. lineMask.fillColor = [UIColor whiteColor].CGColor;
  223. lineMask.fillRule = kCAFillRuleEvenOdd;
  224. lineLayer.transform = CATransform3DMakeRotation((2 * M_PI / self.lineCount) * (i + 1), 0.0, 0.0, 1.0);
  225. lineLayer.mask = lineMask;
  226. [tmpArr addObject: lineLayer];
  227. }
  228. _lines = [NSArray<CAShapeLayer *> arrayWithArray:tmpArr];
  229. }
  230. return _lines;
  231. }
  232. -(void)setSelected:(BOOL)selected{
  233. [super setSelected:selected];
  234. if (selected) {
  235. [self makeChosenAnimation];
  236. }else{
  237. [self makeUnchosenAnimation];
  238. }
  239. }
  240. @end