这是本章的第二个 demo,下面这个案例中,我把线条动画和数学知识结合在了一起。通过这个案例,可以很好地向你展示如何自己归纳出一个数学公式,并把它用到一个自定义动画中。
首先,我们还是先看最终效果 :
OK,可以看到随着手指在屏幕上滑动距离的改变,线条一开始逐渐靠拢,到达一定位置后开始弯曲,最终合并成了一个圆。顺便一提,我已经把这个动画封装到了一个上拉、下拉刷新的控件中,并且用在了大象公会这款独立开发的 App 中。 你可以提前下载下来一睹实际效果。
CALayer
@property(nonatomic,assign)CGFloat progress;
无论你是通过手指滑动产生偏移量,还是滑动 UISlider 改变一个数值,最终都将转化到这个属性的改变。然后,在这个属性的 setter 方法里,我们让 layer 去实时地重绘,就像这样:
-(void)setProgress:(CGFloat)progress{
self.curveLayer.progress = progress;
[self.curveLayer setNeedsDisplay];
}
至于重绘的算法,属于细节上要考虑的事了。我们做一个动画的步骤都是先考虑宏观,再去考虑细节上的实现。就像开发一个 App 一样,一开始肯定是先考虑架构,再去往这个框架里添砖加瓦,修修补补。现在,我们对这个动画的整体思路已经清楚了,下面开始深入到细节去思考具体算法的实现。我把这个动画分成了两个阶段:0~0.5 和 0.5~1.0。 这是什么意思?
moveToPoint
,addLineToPoint
其实你只要愿意动笔在纸上尝试推演一番,并不难求得这两个点的运动公式:
yA = H/2 + h + (1-2*progress) * (H/2 - h)
yB = H/2 + (1-2*progress) * (H/2 - h)
接下来是动画的第二阶段 0.5~1.0。这个阶段有些许复杂:「B 点保持不动,A 点继续运动到 B 的位置,同时,在顶部根据当前的进度再画出圆弧」。视觉上给人的感觉就好像尾巴在逐渐缩短,头部在慢慢弯曲。同样的,我只能以图片的形式向你展示。
在这个过程中,我们不难先求得 A 点的坐标是:
yA = H/2 + h - h*(progress - 0.5) *2
- (void)addArcWithCenter:(CGPoint)center radius:(CGFloat)radius startAngle:(CGFloat)startAngle endAngle:(CGFloat)endAngle clockwise:(BOOL)clockwise NS_AVAILABLE_IOS(4_0);
这个方法绘制出圆弧。具体思路是:以 CGPointMake(self.frame.size.width/2,self.frame.size.height/2)
为圆心,10
为半径,按顺时针方向,从 M_PI(90°)
的起始角度,画到 2*M_PI
- 关于
- (void)addArcWithCenter:(CGPoint)center radius:(CGFloat)radius startAngle:(CGFloat)startAngle endAngle:(CGFloat)endAngle clockwise:(BOOL)clockwise;
- 方法的解释:如果设置开始角度均为 0 ,结束角度均为 π。那么设置 clockwise(顺时针) 为 YES 时,画出的是下半段圆弧;反之, 设置 clockwise(顺时针) 为 NO 时,画出的是上半段圆弧;
以上,我们只完成了一条线段的整个过程。同理,也能获得另一条线段的绘制算法。最后,别忘了线段顶端还有个箭头。绘制箭头的算法 Gallery 4.1:我们以 B 点作为箭头的起始起点,斜向左下方 30° 角延长 3 个单位。弯曲之后也同理,只需要额外加上线段转过的角度即可。
相应的代码就是:
[arrowPath moveToPoint:pointB];
[arrowPath addLineToPoint:CGPointMake(pointB.x - 3*(cosf(Degree)), pointB.y + 3*(sinf(Degree)))];
[curvePath1 appendPath:arrowPath];
OK,这一个 demo 的分析到这里就结束了。完整代码可以在本书对应的 Github Repo 中下载。