http://www.jianshu.com/p/909ffa37dffa
自己想好好学习动画,在这个方向上发展,于是想总结一下自己学习这方面遇到的问题和收获与大家分享,我会一直不断的分享我学到的东西,会跟大家分享自己在学习动画过程中搞懂的一些动画原理,在下一篇会跟大家分享一下注水动画的实现,希望得到大家的支持,也希望能够一直把这个专题写下去。
1. 圆弧动画
经常我们会看到一个圆形加载动画,圆弧大小总是在变化着的,例如芒果TV的加载动画,众乘巴士的加载动画。这种动画原理类似的还出现在一些一些仪表盘的动画中,例如腾讯管家的测试WIFI速度功能,常常是一开始从0的位置或者是一个中间一个位置开始,做一段圆弧大小增缩的动画。
通常这种动画有以下几个共同点:
- 一开始出现的时候圆弧都只是圆的一部分
- 圆弧不断的向圆扩充,起始点和结束点也在不断的变化接下来,我们就以以下这个加载动画为例子,来说一下这类动画的原理,分享一下我的拙见。GitHub传送门
2. 拆分动画
我们通过这张GIF高清无码图,看到一个圆弧从一开始慢慢从0变到0.5,在从0.5变到1的过程(这他妈不是废话吗?!),我们来看一下这两个阶段的动画分别做了什么事情:
- 0 -> 0.5 :起始点位置在0处,结束点从0慢慢变到0.5。
- 0.5 -> 1: 结束点从0.5处出发,用原来的速度走到1.0的位置,但是在这个阶段开始的时候,起始点也动了,而且速度还比结束点运动的速度快,在结束点走到1.0的时候起始点已经追上了结束点的位置。
于是我们得出结论,一开始起始点不动,结束点动,当结束点走到一半的时候起始点发现不对劲了,也开始动,而且动的速度还比结束点快,在终点1.0处起始点追上了结束点 。
3. 实现
(1) CAShapeLayer
要实现这个动画,首先我们得借助CAShapeLayer来做一个圆。在官方文档的第一句话中,CAShapeLayer是这样呗描述的:
/*
*The shape layer draws a cubic Bezier spline in its coordinate space.
* The spline is described using a CGPath object and may have both fill
* and stroke components (in which case the stroke is composited over
* the fill). The shape as a whole is composited between the layer's
* contents and its first sublayer.
*/
CAShapeLayer 是在坐标系内绘制贝塞尔曲线的,通过绘制贝塞尔曲线,设置为shape的path,来绘制各种各样的形状,关于CAShapeLayer的介绍需要的可以自行简书,上面有很详细的介绍,我就不在这里赘述。在这个Gif中,底部有一个灰色的圆,上面盖着一个带有动画的红色的圆,我们分别用CAShapeLayer来创建这两个形状。
/// 底部的灰色layer
CAShapeLayer *bottomShapeLayer = [CAShapeLayer layer];
bottomShapeLayer.strokeColor = [UIColor colorWithRed:229/255.0 green:229/255.0 blue:229/255.0 alpha:1].CGColor;
bottomShapeLayer.fillColor = [UIColor clearColor].CGColor;
bottomShapeLayer.lineWidth = KShapelayerLineWidth;
bottomShapeLayer.path = [UIBezierPath
bezierPathWithRoundedRect:CGRectMake(KShapeLayerMargin, 0, KShapeLayerWidth, KShapeLayerWidth) cornerRadius:KShapeLayerRadius].CGPath; [self.layer addSublayer:bottomShapeLayer];
/// 橘黄色的layer
CAShapeLayer *ovalShapeLayer = [CAShapeLayer layer];
ovalShapeLayer.strokeColor = [UIColor colorWithRed:0.984 green:0.153 blue:0.039 alpha:1.000].CGColor;
ovalShapeLayer.fillColor = [UIColor clearColor].CGColor;
ovalShapeLayer.lineWidth = KShapelayerLineWidth;
ovalShapeLayer.path = [UIBezierPath bezierPathWithRoundedRect:CGRectMake(KShapeLayerMargin, 0,KShapeLayerWidth, KShapeLayerWidth) cornerRadius:KShapeLayerRadius].CGPath;
得到了上面图片的图形,接下来我们要在上面的图片中增加动画,达到我们想要的效果。
(2) 加入动画
从一开始的分析中,我们可以知道,一开始起点位置不变,当结束点运行到0.5的时候以结束点2倍的速度运动,在结束点走一半的路径时间内走完了整个圆。而结束点始终如一的在做匀速运动。
- 起始点:要么一开始不动,一动就是人家的2倍速度,追上人家。
- 结束点:始终以相同的速度匀速运动。
这里实现这个动画有好几种方法,其中一种是让结束点先运动1半,然后这时候起始点开始运动,速度是结束点的2倍,结束点也在0.5的位置继续以原来的速度运动,这种也是从gif图上最直观看到的动画效果。但是在这里我选择了投机取巧,我让起始点一开始也运动,并且是以结束点的速度的两倍,在结束点一开始运动的时候也跟着运动。那么这里就奇怪了,一开始起始点就运动的话,并且速度比结束点大,是不可能走出一段圆形一半的圆弧的。这里,我们就需要让起始点辛苦点了,在相同的时间内要跑结束点运动路径的两倍,结束点在圆形上跑一圈,结束点要在相同的时间里跑两圈,并且一开始第一圈还必须在看不到的地方跑,也就是在起跑线之前跑一圈,这可苦了起始点了,但谁让您老人家跑得太快了呢。
我们这里设置strokeStart的初始值和结束值为 -1.0 和 1.0 ,而 strokeEnd 的初值和最终值为0.0和1.0 ,由于动画时间相等,在strokeEnd的值为0.5的时候,strokeStart刚好从 -1.0到0.0,也就是我们一开始看到的效果,之后由于strokeStart的运动速度是strokeEnd的两倍,所以能在接下来的路程中追赶上。
/// 起点动画
CABasicAnimation * strokeStartAnimation = [CABasicAnimation animationWithKeyPath:@"strokeStart"];
strokeStartAnimation.fromValue = @(-1);
strokeStartAnimation.toValue = @(1.0);
/// 终点动画
CABasicAnimation * strokeEndAnimation = [CABasicAnimation animationWithKeyPath:@"strokeEnd"];
strokeEndAnimation.fromValue = @(0.0);
strokeEndAnimation.toValue = @(1.0);
/// 组合动画
CAAnimationGroup *animationGroup = [CAAnimationGroup animation];
animationGroup.animations = @[strokeStartAnimation, strokeEndAnimation];
animationGroup.duration = KAnimationDurationTime;
animationGroup.repeatCount = CGFLOAT_MAX;
animationGroup.fillMode = kCAFillModeForwards;
animationGroup.removedOnCompletion = NO;
[ovalShapeLayer addAnimation:animationGroup forKey:nil];
(3) 分割点效果
利用CAShapeLayer的lineDashPattern属性,我们可以轻松的设置出分割点的效果,加上原来的动画,就能做到我们想要的效果
ovalShapeLayer.lineDashPattern = @[@6,@3];