今天我们来学习一下,GitHub上一个很好的动画演示:
https://github.com/Glow-Inc/CADisplayLinkDemo
源码可以直接去下载。
其中用到的主要的动画曲线是spring 动画。
http://www.renfei.org/blog/ios-8-spring-animation.html
参考这篇,可以了解参数的意义
比起上面的这个更值得讲的是这个BlockView的实现:
#import "BlockView.h"
@interface BlockView()
@property (strong, nonatomic) CADisplayLink *displayLink;
@property (nonatomic) CGFloat from;
@property (nonatomic) CGFloat to;
@property (nonatomic) BOOL animating;
@end
@implementation BlockView
- (void)startAnimationFrom:(CGFloat)from to:(CGFloat)to
{
self.from = from;
self.to = to;
self.animating = YES;
if (self.displayLink == nil) {
self.displayLink = [CADisplayLink displayLinkWithTarget:self selector:@selector(tick:)];
[self.displayLink addToRunLoop:[NSRunLoop currentRunLoop]
forMode:NSDefaultRunLoopMode];
}
}
- (void)completeAnimation
{
self.animating = NO;
[self.displayLink invalidate];
self.displayLink = nil;
}
- (void)tick:(CADisplayLink *)displayLink
{
[self setNeedsDisplay];
}
- (void)drawRect:(CGRect)rect
{
CALayer *layer =self.layer.presentationLayer;
CGFloat progress = 1;
if (!self.animating) {
progress = 1;
} else {
progress = 1 - (layer.position.y - self.to) / (self.from - self.to);
}
CGFloat height = CGRectGetHeight(rect);
CGFloat deltaHeight = height / 2 * (0.5 - fabs(progress - 0.5));
NSLog(@"delta:%f", deltaHeight);
CGPoint topLeft = CGPointMake(0, deltaHeight);
CGPoint topRight = CGPointMake(CGRectGetWidth(rect), deltaHeight);
CGPoint bottomLeft = CGPointMake(0, height);
CGPoint bottomRight = CGPointMake(CGRectGetWidth(rect), height);
UIBezierPath* path = [UIBezierPath bezierPath];
[[UIColor blueColor] setFill];
[path moveToPoint:topLeft];
[path addQuadCurveToPoint:topRight controlPoint:CGPointMake(CGRectGetMidX(rect), 0)];
[path addLineToPoint:bottomRight];
[path addQuadCurveToPoint:bottomLeft controlPoint:CGPointMake(CGRectGetMidX(rect), height - deltaHeight)];
[path closePath];
[path fill];
}
@end
CADisplayLink
是相当于动画中的计时器,我们看可以看到:- (void)startAnimationFrom:(CGFloat)from to:(CGFloat)to
{
self.from = from;
self.to = to;
self.animating = YES;
if (self.displayLink == nil) {
self.displayLink = [CADisplayLink displayLinkWithTarget:self selector:@selector(tick:)];
[self.displayLink addToRunLoop:[NSRunLoop currentRunLoop]
forMode:NSDefaultRunLoopMode];
}
}
- (void)tick:(CADisplayLink *)displayLink
{
[self setNeedsDisplay];
}
不断去调用setNeesDisplay,众所周知,就是调起重绘:
- (void)drawRect:(CGRect)rect
{
CALayer *layer =self.layer.presentationLayer;
CGFloat progress = 1;
if (!self.animating) {
progress = 1;
} else {
progress = 1 - (layer.position.y - self.to) / (self.from - self.to);
}
CGFloat height = CGRectGetHeight(rect);
CGFloat deltaHeight = height / 2 * (0.5 - fabs(progress - 0.5));
NSLog(@"delta:%f", deltaHeight);
CGPoint topLeft = CGPointMake(0, deltaHeight);
CGPoint topRight = CGPointMake(CGRectGetWidth(rect), deltaHeight);
CGPoint bottomLeft = CGPointMake(0, height);
CGPoint bottomRight = CGPointMake(CGRectGetWidth(rect), height);
UIBezierPath* path = [UIBezierPath bezierPath];
[[UIColor blueColor] setFill];
[path moveToPoint:topLeft];
[path addQuadCurveToPoint:topRight controlPoint:CGPointMake(CGRectGetMidX(rect), 0)];
[path addLineToPoint:bottomRight];
[path addQuadCurveToPoint:bottomLeft controlPoint:CGPointMake(CGRectGetMidX(rect), height - deltaHeight)];
[path closePath];
[path fill];
}
这里用了presentationLayer获取position,并且计算差值,得到左上,右上,左下,右下四个点
然后用贝塞尔曲线去画出当前的曲线效果。
所以一个良好的绘图加动画我们就可以参考上面的实现。