本人錄製技術視頻地址:https://edu.csdn.net/lecturer/1899 歡迎觀看。
最近比較忙碌,有一段時間沒有更新自己的博客了。這一節爲大家介紹的動畫特效是: 仿支付寶轉賬動畫。 先看看最終效果圖。
處理這種多變的動畫,我們的分析思路就是各個擊破。在這裏,我將這個動畫分解爲三組進行處理:
1. 一小段弧線的運動,在效果圖中轉了3圈。
2. 繪製一個完整的圓,在效果圖中轉了1圈。
3. 白色的打鉤動畫。
關於第2點,我在之前的博客中已經進行了詳細的說明工作,我就不做解釋了,如果大家感興趣的話,請參照:CALayer的needsDisplayForKey方法使用說明 這篇博客。
現在我們來分析第一點:
我們知道,繪製圓弧的方法如下:
CGContextAddArc(CGContextRef __nullable c, CGFloat x, CGFloat y,
CGFloat radius, CGFloat startAngle, CGFloat endAngle, int clockwise)
第一個參數是繪製的上下文。
第二,三個參數是圓心。
第四個參數是圓的半徑。
第五、六個參數是繪製圓的起始角度和終止角度。
第六個參數是決定繪製方向(逆時針還是順時針)。
我們這裏所關注的是 startAngle和endAngle, 繪製的起始點是正上方,所以剛開始的時候,startAngle和endAngle均爲 -90°。
注意觀察可以看出,startAngle在前半圓的運動過程中比 endAngle塊,所以在這個過程中,弧線的長度越來越大。startAngle在後半圓的運動過程中比 endAngle慢,所以endAngle會慢慢趕上startAngle, 當繪製完整個圓的時候,兩者又在正上方那點重合了。分析圖如下:
有了這一層分析,就可以緊接着分析兩個小球的運動函數了。假設紅色小球在運動過程中是一直保持勻速狀態。那麼黃色小球在前半段的運動過程中速度相對而言慢一點,而在後半段的運動過程中速度相對而言快一點。體現在數學函數上面就是,前半段,黃色小球的直線斜率小於紅色小球,而後半段,黃色小球的直線斜率大於紅色小球。
函數分析示意圖:
有了上面的分析步奏,我們就可以書寫代碼,進行繪製了。
- (void)drawInContext:(CGContextRef)ctx {
CGContextSetLineWidth(ctx, 5.0f);
CGContextSetLineCap(ctx, kCGLineCapRound);
CGContextSetStrokeColorWithColor(ctx, [UIColor blueColor].CGColor);
if (self.roundTwo) {
CGContextAddArc(ctx, CGRectGetWidth(self.bounds) * 0.5, CGRectGetHeight(self.bounds) * 0.5, CGRectGetWidth(self.bounds) * 0.5 - 5, -M_PI_2, 3 * M_PI / 2 * self.progress, 0);
} else {
// 注意這裏其實角度和終止角度的計算
CGFloat startAngle, endAngle;
if (self.progress <= 0.5) {
startAngle = 5 * M_PI / 3 * self.progress - M_PI_2;
endAngle = 2 * M_PI * self.progress - M_PI_2;
} else {
startAngle = 7 * M_PI / 3 * self.progress - 5 * M_PI / 6;
endAngle = 2 * M_PI * self.progress - M_PI_2;
}
CGContextAddArc(ctx, CGRectGetWidth(self.bounds) * 0.5, CGRectGetHeight(self.bounds) * 0.5, CGRectGetWidth(self.bounds) * 0.5 - 5, startAngle, endAngle, 0);
}
CGContextStrokePath(ctx);
}
接下來,我們分析怎麼繪製打鉤動畫。
從圖中的效果圖,我們可以看出,打鉤動畫就是一段路徑。只要我們實現準備好相關的路徑信息,就可以方便的繪製出來了。分析圖如下:
繪製打鉤的代碼如下:
- (void)startCheck {
CGFloat viewHeight = self.frame.size.height;
CGFloat viewWidth = self.frame.size.width;
UIBezierPath *strokePath = [UIBezierPath bezierPath];
[strokePath moveToPoint:CGPointMake(viewWidth * 0.25, viewHeight * 0.65)];
[strokePath addLineToPoint:CGPointMake(viewWidth * 0.4, viewHeight * 0.75)];
[strokePath addLineToPoint:CGPointMake(viewWidth * 0.75, viewHeight * 0.25)];
CAShapeLayer *checkLayer = [CAShapeLayer layer];
checkLayer.frame = self.bounds; // 這樣就可以相對於父layer計算path了。
checkLayer.strokeColor = [UIColor whiteColor].CGColor;
checkLayer.fillColor = [UIColor clearColor].CGColor;
checkLayer.lineWidth = 10;
checkLayer.lineCap = kCALineCapRound;
checkLayer.lineJoin = kCALineJoinRound;
checkLayer.path = strokePath.CGPath;
[self addSublayer:checkLayer];
CABasicAnimation *checkAnim = [CABasicAnimation animationWithKeyPath:@"strokeEnd"];
checkAnim.fromValue = @(0.0);
checkAnim.toValue = @(1.0);
checkAnim.duration = 1.0f;
checkAnim.timingFunction = [CAMediaTimingFunction functionWithName:kCAMediaTimingFunctionEaseInEaseOut];
checkAnim.delegate = self;
[checkAnim setValue:@"check_animation" forKey:@"check_name"];
[checkLayer addAnimation:checkAnim forKey:nil];
}
至此,這個動畫效果就算完成了。