iOS 复杂动画的简单原理(一)

来源:互联网 发布:音乐国家知乎 编辑:程序博客网 时间:2024/04/29 03:51

http://www.jianshu.com/p/909ffa37dffa

自己想好好学习动画,在这个方向上发展,于是想总结一下自己学习这方面遇到的问题和收获与大家分享,我会一直不断的分享我学到的东西,会跟大家分享自己在学习动画过程中搞懂的一些动画原理,在下一篇会跟大家分享一下注水动画的实现,希望得到大家的支持,也希望能够一直把这个专题写下去。

1. 圆弧动画

经常我们会看到一个圆形加载动画,圆弧大小总是在变化着的,例如芒果TV的加载动画,众乘巴士的加载动画。这种动画原理类似的还出现在一些一些仪表盘的动画中,例如腾讯管家的测试WIFI速度功能,常常是一开始从0的位置或者是一个中间一个位置开始,做一段圆弧大小增缩的动画。


QQ20151229-0@2x.png


通常这种动画有以下几个共同点:

  1. 一开始出现的时候圆弧都只是圆的一部分
  2. 圆弧不断的向圆扩充,起始点和结束点也在不断的变化接下来,我们就以以下这个加载动画为例子,来说一下这类动画的原理,分享一下我的拙见。GitHub传送门

gif3.gif

2. 拆分动画

我们通过这张GIF高清无码图,看到一个圆弧从一开始慢慢从0变到0.5,在从0.5变到1的过程(这他妈不是废话吗?!),我们来看一下这两个阶段的动画分别做了什么事情:

  1. 0 -> 0.5 :起始点位置在0处,结束点从0慢慢变到0.5。
  2. 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;

QQ20151229-1@2x.png


得到了上面图片的图形,接下来我们要在上面的图片中增加动画,达到我们想要的效果。

(2) 加入动画

从一开始的分析中,我们可以知道,一开始起点位置不变,当结束点运行到0.5的时候以结束点2倍的速度运动,在结束点走一半的路径时间内走完了整个圆。而结束点始终如一的在做匀速运动。

  1. 起始点:要么一开始不动,一动就是人家的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];

gif2.gif

(3) 分割点效果

利用CAShapeLayer的lineDashPattern属性,我们可以轻松的设置出分割点的效果,加上原来的动画,就能做到我们想要的效果
ovalShapeLayer.lineDashPattern = @[@6,@3];


0 0
原创粉丝点击