iOS核心动画iOS 核心动画iOS学习开发

加载动画分析

2016-07-28  本文已影响646人  宫城_

原文地址:http://zeeyang.com/2016/07/27/loadingAnimation-0727/

好久没写动画了...最近扒了下以前没有写的动画效果,想想从最老的开始写吧,之前看到的版本是用Swift写的,没仔细找有没有OC版的,所以干脆自己练习一下吧,我们先来看看效果:
(这里三角形是旋转动画,但是Gif录出来看上去是抖了两下...)


可以直接run下代码,看下效果:https://github.com/Yuzeyang/GCLoadingAnimation/tree/master/GCLoadingAnimationOne

下面我来分析下过程

这个动画的实现只用到了UIBezierPathCABasicAnimationCALayer

从Gif里面可以看到这个动画分为以下几个步骤:
1.从无到圆
2.圆x轴方向拉伸和y轴方向拉伸
3.“长出”三角形的三个角
4.三角形旋转
5.画两条边框
6.水面上涨动画
7.中间矩形放大至全屏
8.中间logo跟着出现

0x00 从无到圆

这个比较简单,只要设定起始的size为0和设定默认圆半径大小,用+ bezierPathWithOvalInRect:方法画圆

+ (instancetype)bezierPathWithRoundedRect:(CGRect)rect cornerRadius:(CGFloat)cornerRadius

UIBezierPath *startPath = [self circleStartPath];

UIBezierPath *endPath = [UIBezierPath bezierPathWithOvalInRect:CGRectMake(GCLoadingLayerCenterX - GCCircleRadius, GCLoadingLayerCenterY - GCCircleRadius, GCCircleRadius*2, GCCircleRadius*2)];

将最后圆的path设为circleLayerpath

self.circleLayer = [CAShapeLayer layer];

self.circleLayer.path = endPath.CGPath;

self.circleLayer.fillColor = [UIColor orangeColor].CGColor;
[self addSublayer:self.circleLayer];

然后加上动画,因为我们修改的是path,所以我们animationkeyPathpath(后面也是),设定起始值为startPath.CGPath

CABasicAnimation *circleAnimation = [CABasicAnimation animationWithKeyPath:@"path"];

circleAnimation.fromValue = (__bridge id _Nullable)(startPath.CGPath);

circleAnimation.timingFunction = [CAMediaTimingFunction functionWithName:kCAMediaTimingFunctionEaseOut];
circleAnimation.duration = 0.2f;
circleAnimation.fillMode = kCAFillModeForwards;
circleAnimation.delegate = self;
circleAnimation.removedOnCompletion = NO;
[circleAnimation setValue:@"circleAnimation" forKey:@"animationName"];
[self.circleLayer addAnimation:circleAnimation forKey:nil];

0x01 圆x轴方向拉伸和y轴方向拉伸

这里我们的keyPath不用transform.scale.x/y,因为缩放之后,圆心会改变,看上去有偏移,这样动画写起来更复杂,所以我们干脆直接用拉伸后的path来做动画
创建x轴、y轴拉伸后的path,然后加到animation里面

UIBezierPath *scaleXPath = [UIBezierPath bezierPathWithOvalInRect:CGRectMake(GCLoadingLayerCenterX - GCCircleRadius*1.1, GCLoadingLayerCenterY - GCCircleRadius, GCCircleRadius*2.2, GCCircleRadius*2)];
UIBezierPath *scaleYPath = [UIBezierPath bezierPathWithOvalInRect:CGRectMake(GCLoadingLayerCenterX - GCCircleRadius, GCLoadingLayerCenterY - GCCircleRadius*1.1, GCCircleRadius*2, GCCircleRadius*2.2)];

​CABasicAnimation *circleScaleXOneAnimation = [CABasicAnimation animationWithKeyPath:@"path"];

circleScaleXOneAnimation.fromValue = (__bridge id _Nullable)(self.circleLayer.path);
circleScaleXOneAnimation.toValue = (__bridge id _Nullable)(scaleXPath.CGPath);
circleScaleXOneAnimation.duration = 0.2f;
circleScaleXOneAnimation.beginTime = 0.0;
​```

一共四个`CABasicAnimation`对象,然后我们将这些动画加到`CAAnimationGroup`里

CAAnimationGroup *animationGroup = [CAAnimationGroup animation];

animationGroup.animations = @[circleScaleXOneAnimation,circleScaleXTwoAnimation,circleScaleYOneAnimation,circleScaleYTwoAnimation];

animationGroup.duration = circleScaleYTwoAnimation.beginTime + circleScaleYTwoAnimation.duration;
animationGroup.delegate = self;
[animationGroup setValue:@"circleScaleAnimation" forKey:@"animationName"];

[self.circleLayer addAnimation:animationGroup forKey:nil];

##0x02 “长出”三角形的三个角
实际上三角形在等到圆形出现或者圆形拉伸完之后就已经在那了,“长出角”的感觉实际上只是改变了绘制的三个点的位置,首先我们根据圆的半径画出三角形

UIBezierPath *originTrianglePath = [UIBezierPath bezierPath];
[originTrianglePath moveToPoint:[self triangleLeftPointWithScale:1.0]];

[originTrianglePath addLineToPoint:[self triangleRightPointWithScale:1.0]];
[originTrianglePath addLineToPoint:[self triangleTopPointWithScale:1.0]];
[originTrianglePath closePath];

self.triangleLayer = [CAShapeLayer layer];
self.triangleLayer.path = originTrianglePath.CGPath;
self.triangleLayer.fillColor = [UIColor orangeColor].CGColor;

[self addSublayer:self.triangleLayer];

然后改变左边点的位置

UIBezierPath *blowUpLeftTrianglePath = [UIBezierPath bezierPath];
[blowUpLeftTrianglePath moveToPoint:[self triangleLeftPointWithScale:1.2]];

[blowUpLeftTrianglePath addLineToPoint:[self triangleRightPointWithScale:1.0]];
[blowUpLeftTrianglePath addLineToPoint:[self triangleTopPointWithScale:1.0]];

[blowUpLeftTrianglePath closePath];

也加上`path`的动画

CABasicAnimation *blowUpLeftAnimation = [CABasicAnimation animationWithKeyPath:@"path"];
blowUpLeftAnimation.fromValue = (__bridge id _Nullable)(self.triangleLayer.path);

blowUpLeftAnimation.toValue = (__bridge id _Nullable)(blowUpLeftTrianglePath.CGPath);

blowUpLeftAnimation.duration = 0.2f;
blowUpLeftAnimation.beginTime = 0.0;

右边和上边的点同理,然后也一起加到`CAAnimationGroup`里
##0x03 三角形旋转
旋转就比较简单了,只要根据z轴旋转设定的角度即可

CABasicAnimation rotationAniamtion = [CABasicAnimation animationWithKeyPath:@"transform.rotation.z"];
rotationAniamtion.toValue = @(M_PI
2);

rotationAniamtion.duration = 0.4;

rotationAniamtion.fillMode = kCAFillModeForwards;
rotationAniamtion.delegate = self;
rotationAniamtion.beginTime = CACurrentMediaTime();
[rotationAniamtion setValue:@"rotationAniamtion" forKey:@"animationName"];

[self.triangleLayer addAnimation:rotationAniamtion forKey:nil];

##0x04 画两条边框
这两个边框绘制方法是一模一样的,只是中间有个时间间隔而已

CAShapeLayer *layer = [CAShapeLayer layer];

layer.path = rectPath.CGPath;
layer.lineWidth = 5;
layer.strokeColor = color;
layer.fillColor = [UIColor clearColor].CGColor;
[self addSublayer:layer];

CABasicAnimation *rectAniamtion = [CABasicAnimation animationWithKeyPath:@"strokeEnd"];
rectAniamtion.fromValue = @(0.0);
rectAniamtion.toValue = @(1.0);
rectAniamtion.duration = 0.8;
rectAniamtion.delegate = self;
if (animationValue.length) {
[rectAniamtion setValue:@"rectAniamtion" forKey:@"animationName"];
}
[layer addAnimation:rectAniamtion forKey:nil];

return rectAniamtion;
}

间隔的话,我们直接调用`- performSelector: withObject: afterDelay:`来延迟执行第二条边框的绘制就好
##0x05 水面上涨动画
这个动画的关键就是用`- addCurveToPoint: controlPoint1: controlPoint2:`方法来画出水波的线,这个方法主要是利用`controlPoint1`和`controlPoint2`这两个点来控制弧度方向,如图:
![](http:https://img.haomeiwen.com/i744236/1f22283e0d7fe7de.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240)
然后我们只需要交叉改变 `controlPoint1`和`controlPoint2`这两个点在上下的位置和`startPoint`和`endPoint`的位置,就能感觉水面上涨的感觉

NSMutableArray <UIBezierPath *> *waterPathArray = [NSMutableArray array];
for (NSInteger i = 0; i < 11; i++) {

UIBezierPath water = [self water:i % 2 == 0 ? YES : NO withProgress:0.1i];

[waterPathArray addObject:water];

}

创建完毕`path`之后,将`anmations`放到`CAAnimationGroup`
里面

CAAnimationGroup *group = [CAAnimationGroup animation];

group.animations = animationArray;
group.duration = [animationArray lastObject].beginTime + [animationArray lastObject].duration;
group.fillMode = kCAFillModeForwards;
group.removedOnCompletion = NO;
group.delegate = self;
[group setValue:@"waterAnimation" forKey:@"animationName"];
[self.waterLayer addAnimation:group forKey:nil];
}

##0x06 中间矩形放大至全屏
和前面一样,创建好全屏大小的`path`之后,然后加上动画即可
##0x07 中间logo跟着出现
这个改变`bounds`即可

CALayer *logoLayer = [CALayer layer];
logoLayer.contents = (__bridge id _Nullable)([UIImage imageNamed:@"logo.jpg"].CGImage);


logoLayer.frame = CGRectMake(GCLoadingLayerCenterX, GCLoadingLayerCenterY, 0, 0);
[self addSublayer:logoLayer];

CABasicAnimation *logoAnimation = [CABasicAnimation animationWithKeyPath:@"bounds"];
logoAnimation.toValue = [NSValue valueWithCGRect:CGRectMake(GCLoadingLayerCenterX, GCLoadingLayerCenterY, 100, 120)];

logoAnimation.duration = 0.2;

logoAnimation.beginTime = 0.0;
logoAnimation.removedOnCompletion = NO;
logoAnimation.fillMode = kCAFillModeForwards;
[logoLayer addAnimation:logoAnimation forKey:nil];

这个加载动画的缺点就是在加载时没有可定制化的�形状,只能修改圆形等的颜色,如果要改变形状,可能会涉及到动效的改动,所以这个动画只能作为学习分析参考
上一篇 下一篇

猜你喜欢

热点阅读