爱奇异更新版本之后,那个loading效果比较简洁,却又不失美观,遂仿制一把,以供广大程序员交流学习,效果图如下,
效果图中是垂直效果,但只要在路径那里改一下即可,还有一个缩放动画未添加,留给看官们自己处理
动画分析:
圆弧动画:UIBezierPath +CAShapeLayer
其中UIBezierPath画出左右2边的圆弧,然后用基本动画的storkEnd或storkStart来驱动即可,达到动画效果
代码如下:
CABasicAnimation *animation = [CABasicAnimation animationWithKeyPath:@"strokeEnd"]; animation.duration = 0.7f; animation.fromValue = @(1); animation.toValue = @(0.0); //animation.delegate = self; //animation.repeatCount = MAXFLOAT; // 保持动画结束时的状态 animation.removedOnCompletion = NO; animation.fillMode = kCAFillModeForwards; animation.timingFunction = [CAMediaTimingFunction functionWithName:kCAMediaTimingFunctionEaseInEaseOut]; [animation setValue:@"arcRotateAnim" forKey:@"animationName"]; [self.leftArcLayer addAnimation:animation forKey:@""]; [self.rightArcLayer addAnimation:animation forKey:@""]; //画布旋转动画 CABasicAnimation *BasicAnimation=[CABasicAnimation animationWithKeyPath:@"transform.rotation.z"]; BasicAnimation.toValue=@(M_PI*2); BasicAnimation.duration= 0.7; //BasicAnimation.delegate=self; [BasicAnimation setValue:@"BasicAnimationRotation" forKey:@"animationName"]; [self.layer removeAllAnimations]; [self.layer addAnimation:BasicAnimation forKey:@"BasicAnimationRotation"];
图层分析 :CAShapeLayer在强大之处这里不细说,本案例中主要有4个图层 ,分别使用懒加载实现,代码如下:
/** 重写get属性 @return CAShapeLayer */ -(CAShapeLayer*) leftArcLayer{ if (!_leftArcLayer) { _leftArcLayer = [CAShapeLayer layer]; CGPoint center = CGPointMake(CGRectGetWidth(self.frame) / 2, CGRectGetHeight(self.frame) / 2); UIBezierPath *path = [UIBezierPath bezierPath]; [path addArcWithCenter:center radius:25 startAngle: -PI / 2 endAngle:PI / 2 clockwise:YES]; _leftArcLayer.borderColor = [UIColor blackColor].CGColor; _leftArcLayer.lineWidth = 1.f; _leftArcLayer.path = path.CGPath; _leftArcLayer.fillColor = [UIColor clearColor].CGColor; _leftArcLayer.strokeColor = [UIColor greenColor].CGColor; } return _leftArcLayer; } -(CAShapeLayer*) rightArcLayer{ if (!_rightArcLayer) { _rightArcLayer = [CAShapeLayer layer]; CGPoint center = CGPointMake(CGRectGetWidth(self.frame) / 2, CGRectGetHeight(self.frame) / 2); UIBezierPath *path = [UIBezierPath bezierPath]; [path addArcWithCenter:center radius:25 startAngle: PI / 2 endAngle:-PI / 2 clockwise:YES]; _rightArcLayer.borderColor = [UIColor blackColor].CGColor; _rightArcLayer.lineWidth = 1.f; _rightArcLayer.path = path.CGPath; _rightArcLayer.fillColor = [UIColor clearColor].CGColor; _rightArcLayer.strokeColor = [UIColor greenColor].CGColor; } return _rightArcLayer; } -(CAShapeLayer*) leftPointLayer{ if (!_leftPointLayer) { _leftPointLayer = [CAShapeLayer layer]; CGPoint center = CGPointMake(CGRectGetWidth(self.frame) / 2, CGRectGetHeight(self.frame) / 2); _leftPointLayer.bounds = CGRectMake(0, 0, 2, 2); _leftPointLayer.position = center; _leftPointLayer.backgroundColor = [UIColor greenColor].CGColor; } return _leftPointLayer; } -(CAShapeLayer*) rightPointLayer{ if (!_rightPointLayer) { _rightPointLayer = [CAShapeLayer layer]; CGPoint center = CGPointMake(CGRectGetWidth(self.frame) / 2, CGRectGetHeight(self.frame) / 2); _rightPointLayer.bounds = CGRectMake(0, 0, 2, 2); _rightPointLayer.position = center; _rightPointLayer.backgroundColor = [UIColor greenColor].CGColor; } return _rightPointLayer; }
圆点动画:基本动画中的位移动画:代码如下:
-(void) middlePointMoveAnim{ CABasicAnimation *moveAnim = [CABasicAnimation animationWithKeyPath:@"position"]; CGPoint center = CGPointMake(CGRectGetWidth(self.frame) / 2, CGRectGetHeight(self.frame) / 2); moveAnim.fromValue = [NSValue valueWithCGPoint:center]; moveAnim.toValue = [NSValue valueWithCGPoint:CGPointMake(center.x , center.y - 25)]; moveAnim.duration = 0.5f; moveAnim.delegate = self; moveAnim.fillMode = kCAFillModeForwards; moveAnim.removedOnCompletion = NO; moveAnim.timingFunction = [CAMediaTimingFunction functionWithName:kCAMediaTimingFunctionEaseInEaseOut]; //[self.leftPointLayer removeAllAnimations]; [moveAnim setValue:@"positionAnimation" forKey:@"moveAnim"]; [self.leftPointLayer addAnimation:moveAnim forKey:@""]; CABasicAnimation *moveRightAnim = [CABasicAnimation animationWithKeyPath:@"position"]; moveRightAnim.fromValue = [NSValue valueWithCGPoint:center]; moveRightAnim.toValue = [NSValue valueWithCGPoint:CGPointMake(center.x , center.y + 25)]; moveRightAnim.duration = 0.5f; //moveRightAnim.delegate = self; moveRightAnim.fillMode = kCAFillModeForwards; moveRightAnim.removedOnCompletion = NO; moveRightAnim.timingFunction = [CAMediaTimingFunction functionWithName:kCAMediaTimingFunctionEaseInEaseOut]; //[self.rightPointLayer removeAllAnimations]; [self.rightPointLayer addAnimation:moveRightAnim forKey:@""]; }
全部代码如下:
.h文件
@interface ShapeLayer_BezierParh : UIView<CAAnimationDelegate> @property(nonatomic,strong) CAShapeLayer* leftArcLayer; @property(nonatomic,strong) CAShapeLayer* rightArcLayer; @property(nonatomic,strong) CAShapeLayer* leftPointLayer; @property(nonatomic,strong) CAShapeLayer* rightPointLayer; -(void) startAnim; -(void) middlePointMoveAnim; @end
.m文件:
#import "ShapeLayer_BezierParh.h" #define PI 3.1415926 @implementation ShapeLayer_BezierParh -(instancetype) initWithFrame:(CGRect)frame{ if (self = [super initWithFrame:frame]) { [self.layer addSublayer:self.leftArcLayer]; [self.layer addSublayer:self.rightArcLayer]; [self.layer addSublayer:self.leftPointLayer]; [self.layer addSublayer:self.rightPointLayer]; } return self; } /** 重写get属性 @return CAShapeLayer */ -(CAShapeLayer*) leftArcLayer{ if (!_leftArcLayer) { _leftArcLayer = [CAShapeLayer layer]; CGPoint center = CGPointMake(CGRectGetWidth(self.frame) / 2, CGRectGetHeight(self.frame) / 2); UIBezierPath *path = [UIBezierPath bezierPath]; [path addArcWithCenter:center radius:25 startAngle: -PI / 2 endAngle:PI / 2 clockwise:YES]; _leftArcLayer.borderColor = [UIColor blackColor].CGColor; _leftArcLayer.lineWidth = 1.f; _leftArcLayer.path = path.CGPath; _leftArcLayer.fillColor = [UIColor clearColor].CGColor; _leftArcLayer.strokeColor = [UIColor greenColor].CGColor; } return _leftArcLayer; } -(CAShapeLayer*) rightArcLayer{ if (!_rightArcLayer) { _rightArcLayer = [CAShapeLayer layer]; CGPoint center = CGPointMake(CGRectGetWidth(self.frame) / 2, CGRectGetHeight(self.frame) / 2); UIBezierPath *path = [UIBezierPath bezierPath]; [path addArcWithCenter:center radius:25 startAngle: PI / 2 endAngle:-PI / 2 clockwise:YES]; _rightArcLayer.borderColor = [UIColor blackColor].CGColor; _rightArcLayer.lineWidth = 1.f; _rightArcLayer.path = path.CGPath; _rightArcLayer.fillColor = [UIColor clearColor].CGColor; _rightArcLayer.strokeColor = [UIColor greenColor].CGColor; } return _rightArcLayer; } -(CAShapeLayer*) leftPointLayer{ if (!_leftPointLayer) { _leftPointLayer = [CAShapeLayer layer]; CGPoint center = CGPointMake(CGRectGetWidth(self.frame) / 2, CGRectGetHeight(self.frame) / 2); _leftPointLayer.bounds = CGRectMake(0, 0, 2, 2); _leftPointLayer.position = center; _leftPointLayer.backgroundColor = [UIColor greenColor].CGColor; } return _leftPointLayer; } -(CAShapeLayer*) rightPointLayer{ if (!_rightPointLayer) { _rightPointLayer = [CAShapeLayer layer]; CGPoint center = CGPointMake(CGRectGetWidth(self.frame) / 2, CGRectGetHeight(self.frame) / 2); _rightPointLayer.bounds = CGRectMake(0, 0, 2, 2); _rightPointLayer.position = center; _rightPointLayer.backgroundColor = [UIColor greenColor].CGColor; } return _rightPointLayer; } -(void) startAnim{ CABasicAnimation *animation = [CABasicAnimation animationWithKeyPath:@"strokeEnd"]; animation.duration = 0.7f; animation.fromValue = @(1); animation.toValue = @(0.0); //animation.delegate = self; //animation.repeatCount = MAXFLOAT; // 保持动画结束时的状态 animation.removedOnCompletion = NO; animation.fillMode = kCAFillModeForwards; animation.timingFunction = [CAMediaTimingFunction functionWithName:kCAMediaTimingFunctionEaseInEaseOut]; [animation setValue:@"arcRotateAnim" forKey:@"animationName"]; [self.leftArcLayer addAnimation:animation forKey:@""]; [self.rightArcLayer addAnimation:animation forKey:@""]; //画布旋转动画 CABasicAnimation *BasicAnimation=[CABasicAnimation animationWithKeyPath:@"transform.rotation.z"]; BasicAnimation.toValue=@(M_PI*2); BasicAnimation.duration= 0.7; //BasicAnimation.delegate=self; [BasicAnimation setValue:@"BasicAnimationRotation" forKey:@"animationName"]; [self.layer removeAllAnimations]; [self.layer addAnimation:BasicAnimation forKey:@"BasicAnimationRotation"]; dispatch_time_t delayTime = dispatch_time(DISPATCH_TIME_NOW, (int64_t)(0.8 * NSEC_PER_SEC)); dispatch_after(delayTime, dispatch_get_main_queue(), ^{ [self middlePointMoveAnim]; }); } -(void) middlePointMoveAnim{ CABasicAnimation *moveAnim = [CABasicAnimation animationWithKeyPath:@"position"]; CGPoint center = CGPointMake(CGRectGetWidth(self.frame) / 2, CGRectGetHeight(self.frame) / 2); moveAnim.fromValue = [NSValue valueWithCGPoint:center]; moveAnim.toValue = [NSValue valueWithCGPoint:CGPointMake(center.x , center.y - 25)]; moveAnim.duration = 0.5f; moveAnim.delegate = self; moveAnim.fillMode = kCAFillModeForwards; moveAnim.removedOnCompletion = NO; moveAnim.timingFunction = [CAMediaTimingFunction functionWithName:kCAMediaTimingFunctionEaseInEaseOut]; //[self.leftPointLayer removeAllAnimations]; [moveAnim setValue:@"positionAnimation" forKey:@"moveAnim"]; [self.leftPointLayer addAnimation:moveAnim forKey:@""]; CABasicAnimation *moveRightAnim = [CABasicAnimation animationWithKeyPath:@"position"]; moveRightAnim.fromValue = [NSValue valueWithCGPoint:center]; moveRightAnim.toValue = [NSValue valueWithCGPoint:CGPointMake(center.x , center.y + 25)]; moveRightAnim.duration = 0.5f; //moveRightAnim.delegate = self; moveRightAnim.fillMode = kCAFillModeForwards; moveRightAnim.removedOnCompletion = NO; moveRightAnim.timingFunction = [CAMediaTimingFunction functionWithName:kCAMediaTimingFunctionEaseInEaseOut]; //[self.rightPointLayer removeAllAnimations]; [self.rightPointLayer addAnimation:moveRightAnim forKey:@""]; } -(void)animationDidStop:(CAAnimation *)anim finished:(BOOL)flag{ if([[anim valueForKey:@"animationName"] isEqualToString:@"arcRotateAnim"]){ NSLog(@"144----------------arcRotateAnim:stop %d",flag); if(flag){ dispatch_time_t delayTime = dispatch_time(DISPATCH_TIME_NOW, (int64_t)(0.2 * NSEC_PER_SEC)); dispatch_after(delayTime, dispatch_get_main_queue(), ^{ [self middlePointMoveAnim]; }); } } else if([[anim valueForKey:@"moveAnim"] isEqualToString:@"positionAnimation"]){ NSLog(@"171----------------moveAnim:stop :%d",flag); if(flag){ dispatch_time_t delayTime = dispatch_time(DISPATCH_TIME_NOW, (int64_t)(0.2 * NSEC_PER_SEC)); dispatch_after(delayTime, dispatch_get_main_queue(), ^{ [self startAnim]; }); } } } @end
在vc中的测试代码如下:
-(void) testShapeLayer{ self.bezierParhLayer = [[ShapeLayer_BezierParh alloc] initWithFrame:CGRectMake((CGRectGetWidth(self.view.frame) / 2) - 25, 100, 100, 100)]; //self.bezierParhLayer.backgroundColor = [UIColor wh]; [self.view addSubview:self.bezierParhLayer]; }
将开启动画放在一个按钮下,点击按钮进行动画启动:
[self.bezierParhLayer middlePointMoveAnim];
其中属性:
@property(nonatomic,strong) ShapeLayer_BezierParh* bezierParhLayer;