本帖最后由 小篱 于 2015-12-9 15:59 编辑

1438363772oq26zv7o722b.png
  图中红点就是圆的顶点,由可知,动画开始时,顶点y坐标就是center.y - r,结束时就是在这个基础加上缩放的部分2r * (1 - scale),即center + 2r * (1 - scale)处。

  再看细线,细线的长度可以从阶段3中拿到,动画开始时有长度(下图中灰色线),动画结束时,SS、SE重合,线就消失了(下图中蓝色圆的顶点),如图

1438378vxpgvz9vgvfgqgq.png 细线
  再看粗线,我们假设结束时,粗线的底部位于圆未变形时的圆心处,动画开始时SS、SE重合,线看不到(下图中灰色圆的顶点),动画结束时,SS、SE分别到了不同位置,线就出现了(下图中蓝色线),如图

143837u0qtnnn8jzqurj8n.png 粗线
  还不是很清楚的同学,可以在纸上画一画,画着画着就明白了。

  至此,关键节点的值都找到了。

  下面我们来看一个问题。

  有的同学注意到了,这个圆的缩放和我们常用的不一样,常用的是圆心不变,圆顶点和底点分别向圆心靠拢,形成椭圆的效果。而这个动画中,是圆底点不动,顶点和圆心都向底点靠拢。

  有经验的同学可能要提到一个词了,anchor point ,这个我先截一张官方文档Core Animation Programming Guide中的示意图,图中用旋转transform示意了anchor point和position。

  我们发现,3者的交界是圆的顶点,看来这是一个重要的节点。

  在本示例中,我们假设动画结束时,圆的缩放系数scale为0.8,请看下图

143838c4cvzs4mpw7sy7v4.png
  不严谨的说,transform时,anchor point就是不动的那个点,点基于anchor point进行计算(具体大家还是要看一下Core Animation Programming Guide文档,或它的翻译版)。

  具体到本例中,anchor point应该在圆的底点,这样缩放时,就是底点不动,其他点基于底点计算了。

  理解了anchor point,有的同学写出了这一句。

 

 

  1. self.arcToCircleLayer.anchorPoint = CGPointMake(0.5, 1);
复制代码
  这一句逻辑是对的,x等于0.5即anchor point在x轴上位于圆的中心,y等于1即anchor point在y轴上位于圆的底点,和我们前文中的说到的要求一致。

  但执行时,却会惊喜的发现,圆的位置发生了一次突变,如下图

none.gif
  这是怎么了,想一想,圆的大小与位置可以认为是frame的体现,查看一下CALayer的文档,在frame的Discussion中,有这么一句

 

 

  the frame rectangle is a computed property that is derived from the values in the bounds, anchorPoint and position properties.

也就是说frame是根据bounds、anchorPoint和position这3个属性算出来的,我们没有改变bounds和position,而单单改变了anchorPoint,frame自然也跟着变了。

  怎么解决呢,答案依然在frame的Discussion中,如下

 

 

  When you assign a new value to this property, the layer changes its position and bounds properties to match the rectangle you specified.

文中的this property就是frame,这段文字说明,我们给frame指定新值,layer会自动调整position和bounds。

  那就简单了,我们设置anchor point后,再将圆的frame设置回之前的值,如下

 

 

  1. CGRect frame = self.arcToCircleLayer.frame;
  2. self.arcToCircleLayer.anchorPoint = CGPointMake(0.5, 1);
  3. self.arcToCircleLayer.frame = frame;
复制代码
  篇幅关系,我就不贴大段代码了,完整代码大家可以查看GitHub上OneLoadingAnimation工程的OneLoadingAnimationStep4目录。

  为方便大家查看,我贴了一点代码,说明一下代码的结构,如下

 

 

  1. // 第4阶段
  2. - (void)doStep4 {
  3. [self doStep4a];
  4. [self doStep4b];
  5. [self doStep4c];
  6. }
  7. // 4阶段a:小圆变形
  8. - (void)doStep4a {}
  9.  
  10. // 4阶段b:逐渐消失的竖线
  11. - (void)doStep4b {}
  12.  
  13. // 4阶段c:逐渐出现的竖线
  14. - (void)doStep4c {}
复制代码
  用1、2、3来表示某阶段,用a、b、c来表示阶段内某个动画,这显然不是良好的命名,但在这个示例中,这样也许更清晰。

  篇幅所限,我们在下一篇中再聊后续的阶段,大家有兴趣的话,可以分解一下后面的动画,找一找感觉。

  本文中提到解决问题的思路,相当一部分得益于V.Anton Spraul的《像程序员一样思考》,ISBN号9787115383396,有兴趣的同学可以看一下,写的很好。

  第三篇到这就告一段落了,非常感谢大家的观看,我们下一篇再会。

  阶段4中我简化的部分

  大家仔细查看原动效的话,会发现圆的变化是不规则的。

  原设计更符合直觉,比如大家把球放到地上,拿一个手指去按它,手指按的部分和球与地面接触的部分,变形度肯定是不一样的。

  为了实现简单,我将圆的变化处理成了规则的变化,即从圆渐渐变成了椭圆。

  后续我会单独开一篇和大家聊一下圆不规则变化的实现,敬请期待。

  完整代码

  请参考GitHub上OneLoadingAnimation的OneLoadingAnimationStep3和OneLoadingAnimationStep4目录。

  鸣谢及推荐

  原动效的设计者 moonjoin

  优秀的动效教程Kittens 时间胶囊

  喵神发起的objc中国的动画部分,都是很优秀的译文,衷心为翻译的同学点赞。

  相关链接

  Core Animation Programming Guide

  CALayer Class Reference

  CAShapeLayer Class Reference

  UIBezierPath Class Reference

  相关阅读:

  一款Loading动画的实现思路(一):复杂任务的拆分

  一款Loading动画的实现思路(二):stroke方案

锐亚教育

锐亚教育,游戏开发论坛|游戏制作人|游戏策划|游戏开发|独立游戏|游戏产业|游戏研发|游戏运营| unity|unity3d|unity3d官网|unity3d 教程|金融帝国3|8k8k8k|mcafee8.5i|游戏蛮牛|蛮牛 unity|蛮牛