今天动画的主要用CAShapeLayer和贝塞尔曲线做一个提交的动画,也是没有什么难度的
先简单的介绍下CAShapeLayer
- CAShapeLayer继承自CALayer,可使用CALayer的所有属性
- CAShapeLayer需要和贝塞尔曲线配合使用才有意义。
Shape:形状,贝塞尔曲线可以为其提供形状,而单独使用CAShapeLayer是没有任何意义的。 - 使用CAShapeLayer与贝塞尔曲线可以实现不在view的DrawRect方法中画出一些想要的图形
关于CAShapeLayer和DrawRect的比较
DrawRect:DrawRect属于CoreGraphic框架,占用CPU,消耗性能大
CAShapeLayer:CAShapeLayer属于CoreAnimation框架,通过GPU来渲染图形,节省性能。动画渲染直接提交给手机GPU,不消耗内存
贝塞尔曲线与CAShapeLayer的关系
- CAShapeLayer中shape代表形状的意思,所以需要形状才能生效
- 贝塞尔曲线可以创建基于矢量的路径
- 贝塞尔曲线给CAShapeLayer提供路径,CAShapeLayer在提供的路径中进行渲染。路径会闭环,所以绘制出了Shape
- 用于CAShapeLayer的贝塞尔曲线作为Path,其path是一个首尾相接的闭环的曲线,即使该贝塞尔曲线不是一个闭环的曲线
以上文字来源于网络,哈哈
如果有同学对UIBezierPath不熟悉的请看这里
CAShapeLayer的介绍
上面的介绍也说了贝塞尔曲线给CAShapeLayer提供路径,CAShapeLayer在提供的路径中进行渲染,把路径用以形状的形式展示出来,CAShapeLayer最重要的属性就是下面三个:
//动画的路径
@property(nullable) CGPathRef path;
//描述path路径从哪里开始
@property CGFloat strokeStart;
//描述path路径从哪里结束
@property CGFloat strokeEnd;
这两个值的范围是[0,1],
接下来我们先画一个不会动的 对号 来学习一下 CAShapeLayer
UIBezierPath *bezierPath=[UIBezierPath bezierPath];
[bezierPath moveToPoint:CGPointMake(self.frame.size.width/4, self.frame.size.height/2)];
[bezierPath addLineToPoint:CGPointMake(self.frame.size.width/2, self.frame.size.height/4*3)];
[bezierPath addLineToPoint:CGPointMake(self.frame.size.width/4*3, self.frame.size.height/3)];
CAShapeLayer *shape=[CAShapeLayer layer];
shape.lineWidth=17;
shape.fillColor=[UIColor clearColor].CGColor;
shape.strokeColor=[UIColor colorWithRed:0.76f green:0.89f blue:0.89f alpha:1.00f].CGColor;
shape.lineCap = kCALineCapRound;
shape.lineJoin = kCALineJoinRound;
shape.path=bezierPath.CGPath;
[self.layer addSublayer:shape];
UIBezierPath只是告诉路径给CAShapeLayer,具体这个shape什么样子由CAShapeLayer来决定
所以一些属于lineWidth,fillColor是在shape上设置的,在UIBezierPath上设置无效
补充:lineCap
- kCALineCapButt: 默认格式,不附加任何形状;
- kCALineCapRound: 在线段头尾添加半径为线段 lineWidth 一半的半圆;
- kCALineCapSquare: 在线段头尾添加半径为线段 lineWidth 一半的矩形”
CAShapeLayer的基本用法就是这样接下来就是动画了
动画第一步长方形变圆形
先添加一个checkButton的cornerRadius变为圆形的高度一半的动画
注意
我们知道,使用 CAAnimation 如果不做额外的操作,动画会在结束之后返回到初始状态。或许你会这么设置:
radiusAnimation.fillMode = kCAFillModeForwards; radiusAnimation.removedOnCompletion = NO;
但这不是正确的方式。正确的做法可以参考 WWDC 2011 中的 session 421 - Core Animation Essentials。
Session 中推荐的做法是先显式地改变 Model Layer 的对应属性,再应用动画。这样一来,我们甚至省去了 toValue。
因为 cornerRadius 也是 Animatable 的,所以可以作为 KeyPath 进行动画。首先显式地设定属性的终止状态,为进度条高度的 1/2 : ZMButtonSize().height/2. 设置好起始状态。
static CGSize ZMButtonSize() {
return CGSizeMake(100, 100);
}
-
self.layer.cornerRadius=ZMButtonSize().height/2;
CABasicAnimation *cornerRadiusAnimation=[CABasicAnimation animationWithKeyPath:@"cornerRadius"];
cornerRadiusAnimation.delegate=self;
cornerRadiusAnimation.duration=0.2;
cornerRadiusAnimation.timingFunction = [CAMediaTimingFunction functionWithName:kCAMediaTimingFunctionEaseOut];
cornerRadiusAnimation.fromValue=@(self.frame.size.height/2);[self.layer addAnimation:cornerRadiusAnimation forKey:@"cornerRadiusAnimation"];
在Animation的代理方法里动画一开始让checkButton的bounds改变
-(void)animationDidStart:(CAAnimation *)anim
{
if([[self.layer animationForKey:@"cornerRadiusAnimation"] isEqual:anim])
{
[UIView animateWithDuration:0.6f delay:0.0f usingSpringWithDamping:0.6 initialSpringVelocity:0.0 options:UIViewAnimationOptionCurveEaseOut animations:^{
self.bounds = CGRectMake(0, 0, ZMButtonSize().height, ZMButtonSize().height);
self.backgroundColor=[UIColor colorWithRed:1.00f green:0.80f blue:0.56f alpha:1.00f];
} completion:^(BOOL finished) {
[self.layer removeAllAnimations];
[self checkAnimation];
}];
}
}
动画第二步画会动的对号
在checkButton的bounds改变完成是在checkButton上画一个对号
这时候就用到了CAShapeLayer的 strokeStart, strokeEnd,在动画时设置KeyPath:为"strokeEnd",从0到1,这样一个动画的对号就出来了。
CAShapeLayer *shape=[CAShapeLayer layer];
UIBezierPath *bezierPath=[UIBezierPath bezierPath];
[bezierPath moveToPoint:CGPointMake(self.frame.size.width/4, self.frame.size.height/2)];
[bezierPath addLineToPoint:CGPointMake(self.frame.size.width/2, self.frame.size.height/4*3)];
[bezierPath addLineToPoint:CGPointMake(self.frame.size.width/4*3, self.frame.size.height/3)];
//UIBezierPath只是告诉路径给CAShapeLayer,具体这个shpe什么样子由CAShapeLayer来决定
//所以一些属于lineWidth,fillColor是在shpe上设置的,在UIBezierPath上设置无效
shape.lineWidth=17;
shape.fillColor=[UIColor clearColor].CGColor;
shape.strokeColor=[UIColor colorWithRed:0.76f green:0.89f blue:0.89f alpha:1.00f].CGColor;
shape.lineCap = kCALineCapRound;
shape.lineJoin = kCALineJoinRound;
shape.path=bezierPath.CGPath;
[self.layer addSublayer:shape];
CABasicAnimation *checkAnimation = [CABasicAnimation animationWithKeyPath:@"strokeEnd"];
checkAnimation.duration = 0.5f;
checkAnimation.fromValue = @(0.0f);
checkAnimation.toValue = @(1.0f);
checkAnimation.delegate = self;
[shape addAnimation:checkAnimation forKey:@"checkAnimation"];
理论上,所有描线的动画你都可以用这种方式先指定一个 path 然后改变 strokeEnd, strokeStart 来实现。
如果感觉这篇文章对您有所帮助,顺手点个喜欢,谢谢啦
代码放在了GitHub上大家可以下载。