iOS 動畫篇 (三) CADisplayLink與CoreGraphics實現動畫
阿新 • • 發佈:2018-01-17
posit 其中 sin interface raw gpa cti sha syn
本文主要介紹利用CoreGraphics和CADisplayLink來實現一個註水動畫。來一個效果圖先:
在介紹註水動畫前,先介紹利用CoreGraphics實現進度條的繪制。
一、扇形進度繪制
效果:
代碼如下:
- (instancetype)initWithCoder:(NSCoder *)coder { self = [super initWithCoder:coder]; if (self) { self.arcColor = [UIColor cyanColor]; } return self; } - (void)drawRect:(CGRect)rect { [super drawRect:rect]; CGContextRef context = UIGraphicsGetCurrentContext(); [self.arcColor setFill]; CGFloat startAngle = -M_PI_2; CGFloat endAngle = self.progress * M_PI * 2 + startAngle; CGPoint center = CGPointMake(rect.size.width / 2, rect.size.height / 2); UIBezierPath *path = [UIBezierPath bezierPathWithArcCenter:center radius:rect.size.width / 2 startAngle:startAngle endAngle:endAngle clockwise:YES]; CGContextAddPath(context, path.CGPath); CGContextAddLineToPoint(context, center.x, center.y); CGContextDrawPath(context, kCGPathFill); }- (void)setProgress:(CGFloat)progress { NSLog(@"%g", progress); if (progress > 1) { progress = 1; }else if (progress < 0){ progress = 0; } _progress = progress; dispatch_async(dispatch_get_main_queue(), ^{ [self setNeedsDisplay]; }); }
原理就是根據不同的進度值不停的重新繪制扇形。
二、繪制帶邊緣的扇形進度圖
代碼如下:
@implementation ArcWithTrackProgressView - (instancetype)initWithCoder:(NSCoder *)coder { self = [super initWithCoder:coder]; if (self) { self.backgroundColor = [UIColor clearColor]; self.trackColor = [UIColor cyanColor]; self.progressColor = [UIColor cyanColor]; } return self; } - (void)drawRect:(CGRect)rect { //繪制圈 UIBezierPath *trackPath = [UIBezierPath bezierPathWithOvalInRect:CGRectInset(rect, 2, 2)]; CGContextRef context = UIGraphicsGetCurrentContext(); CGContextSetLineWidth(context, 0.5); CGContextAddPath(context, trackPath.CGPath); [self.trackColor setStroke]; CGContextDrawPath(context, kCGPathStroke);//繪制進度 [self.progressColor setFill]; CGFloat startAngle = - M_PI_2; CGFloat endAngle = self.progress * 2 * M_PI + startAngle; CGPoint center = CGPointMake(CGRectGetWidth(rect) / 2, CGRectGetHeight(rect) / 2); CGFloat radius = CGRectGetHeight(rect) / 2 - 6;//設置半徑 UIBezierPath *progressPath = [UIBezierPath bezierPathWithArcCenter:center radius:radius startAngle:startAngle endAngle:endAngle clockwise:YES]; CGContextAddPath(context, progressPath.CGPath); CGContextAddLineToPoint(context, center.x, center.y); CGContextDrawPath(context, kCGPathFill); } - (void)setProgress:(CGFloat)progress { NSLog(@"%g", progress); if (progress > 1) { progress = 1; }else if (progress < 0){ progress = 0; } _progress = progress; dispatch_async(dispatch_get_main_queue(), ^{ [self setNeedsDisplay]; }); } @end
三、繪制一個圓環進度
效果圖如下:
此效果分為兩步實現,一部分是繪制圓環,一部分是繪制勾。我在這裏使用的CoreGraphics來繪制環,勾的話是利用CAShapeLayer來實現的。代碼如下:
@implementation AnnularProgressView - (instancetype)initWithFrame:(CGRect)frame { self = [super initWithFrame:frame]; if (self) { [self commonSetup]; } return self; } - (instancetype)initWithCoder:(NSCoder *)coder { self = [super initWithCoder:coder]; if (self) { [self commonSetup]; } return self; } - (void)commonSetup { self.arcColor = [UIColor cyanColor]; self.lineWidth = 5.f; //設置shapeLayer CAShapeLayer *tick = [[CAShapeLayer alloc] init]; tick.bounds = self.bounds; tick.position = CGPointMake(CGRectGetWidth(self.bounds) / 2, CGRectGetHeight(self.bounds) / 2); CGFloat width = CGRectGetWidth(self.bounds); CGFloat height = CGRectGetHeight(self.bounds); UIBezierPath *bezierPath = [[UIBezierPath alloc] init]; [bezierPath moveToPoint:CGPointMake(width * 0.25, height * 0.46)]; [bezierPath addLineToPoint:CGPointMake(width * 0.45, height * 0.71)]; [bezierPath addLineToPoint:CGPointMake(width * 0.78, height * 0.29)]; tick.path = bezierPath.CGPath; tick.fillColor = [UIColor clearColor].CGColor; tick.strokeColor = [UIColor cyanColor].CGColor; tick.strokeStart = 0; tick.strokeEnd = 0; tick.lineWidth = self.lineWidth; tick.lineCap = kCALineJoinRound; [self.layer addSublayer:tick]; self.tick = tick; } - (void)drawRect:(CGRect)rect { CGContextRef context = UIGraphicsGetCurrentContext(); CGContextSetLineCap(context, kCGLineCapRound); CGContextSetLineWidth(context, self.lineWidth); [self.arcColor setStroke]; //繪制圓環 CGFloat startAngle = -M_PI_2; CGFloat endAngle = self.progress * M_PI * 2 + startAngle; CGPoint center = CGPointMake(rect.size.width / 2, rect.size.height / 2); UIBezierPath *path = [UIBezierPath bezierPathWithArcCenter:center radius:rect.size.width / 2 - self.lineWidth startAngle:startAngle endAngle:endAngle clockwise:YES]; CGContextAddPath(context, path.CGPath); CGContextDrawPath(context, kCGPathStroke); self.tick.strokeEnd = self.progress;//設置勾的進度 } - (void)setProgress:(CGFloat)progress { NSLog(@"%g", progress); if (progress > 1) { progress = 1; }else if (progress < 0){ progress = 0; } _progress = progress; dispatch_async(dispatch_get_main_queue(), ^{ [self setNeedsDisplay]; }); }
四、註水動畫
效果:
註水動畫的實現主要是通過正余弦函數繪制來實現的。正余弦曲線公式如下:
正弦函數
y=Asin(ωx+φ)+k //正弦函數 y=Acos(ωx+φ)+k //余弦函數
其中
A——振幅,當物體作軌跡符合正弦曲線的直線往復運動時,其值為行程的1/2。 (ωx+φ)——相位,反映變量y所處的狀態。 φ——初相,x=0時的相位;反映在坐標系上則為圖像的左右移動。 k——偏距,反映在坐標系上則為圖像的上移或下移。 ω——角速度, 控制正弦周期(單位角度內震動的次數)。 介紹完公式,接下來直接上代碼:@interface WaveProgressView () @property (nonatomic, assign) CGFloat initialPhase;//初相 @property (nonatomic, strong) CADisplayLink *timer; @end //y=Asin(ωx+φ)+k @implementation WaveProgressView - (instancetype)initWithCoder:(NSCoder *)coder { self = [super initWithCoder:coder]; if (self) { [self commonSetup]; } return self; } - (instancetype)initWithFrame:(CGRect)frame { self = [super initWithFrame:frame]; if (self) { [self commonSetup]; } return self; } - (void)commonSetup { CADisplayLink *timer = [CADisplayLink displayLinkWithTarget:self selector:@selector(moveWave:)]; [timer addToRunLoop:[NSRunLoop mainRunLoop] forMode:NSRunLoopCommonModes]; self.backgroundColor = [UIColor clearColor]; } - (void)drawRect:(CGRect)rect { CGContextRef context = UIGraphicsGetCurrentContext(); UIBezierPath *sinPath = [UIBezierPath bezierPath]; UIBezierPath *cosPath = [UIBezierPath bezierPath]; CGFloat y; CGFloat amplitude = 5;//振幅 CGFloat palstance = M_PI / self.bounds.size.width;//角速度 CGPoint startPoint = CGPointMake(0, CGRectGetHeight(rect)); [sinPath moveToPoint:startPoint]; [cosPath moveToPoint:startPoint]; //正弦曲線 for (CGFloat x = 0.0 ; x <= rect.size.width; x++) { y = amplitude * sin(palstance * x + self.initialPhase); CGPoint point = CGPointMake(x, y + CGRectGetHeight(rect) * (1 - self.progress) - amplitude); [sinPath addLineToPoint:point]; } //余弦曲線 for (int x = 0 ; x <= rect.size.width; x++) { y = amplitude * cos(palstance * x + self.initialPhase); CGPoint point = CGPointMake(x, y + CGRectGetHeight(rect) * (1 - self.progress) - amplitude); [cosPath addLineToPoint:point]; } CGPoint endPoint = CGPointMake(CGRectGetWidth(rect), CGRectGetHeight(rect)); [sinPath addLineToPoint:endPoint]; [cosPath addLineToPoint:endPoint]; [[UIColor lightGrayColor] setFill]; CGContextAddPath(context, sinPath.CGPath); CGContextDrawPath(context, kCGPathFill); [[UIColor cyanColor] setFill]; CGContextAddPath(context, cosPath.CGPath); CGContextDrawPath(context, kCGPathFill); } - (void)moveWave:(CADisplayLink *)timer { self.initialPhase += 0.1; dispatch_async(dispatch_get_main_queue(), ^{ [self setNeedsDisplay]; }); } - (void)setProgress:(CGFloat)progress { NSLog(@"%g", progress); if (progress > 1) { progress = 1; }else if (progress < 0){ progress = 0; } _progress = progress; } - (void)dealloc { [self.timer invalidate]; }
實現原理:設定好曲線的振幅、角速度,然後根據progress來設置正余弦曲線的繪制路徑。利用CADisplayLink來不斷的改變曲線的初相來達到曲線移動的效果。
你可以從這裏下載demo
iOS 動畫篇 (三) CADisplayLink與CoreGraphics實現動畫