拆分動畫
實現一個動畫,有很多種途徑。不管是哪種途徑,都要分析一下動畫由哪幾部分組成,然後將這些部分組合起來。
這是一個簡單的動畫,由兩部分組成:
- 縮放動畫,字體框架大小的縮放
- 旋轉動畫,字體放大到最大時,左右旋轉,實現抖動效果
最後用動畫組將這兩組動畫組合起來就OK了。
旋轉動畫
- (void)rotationAnimation {
CAKeyframeAnimation *rotationAnim = [CAKeyframeAnimation animationWithKeyPath:@"transform.rotation"];
// 動畫時間
rotationAnim.duration =3.0;
//無限次重複
rotationAnim.repeatCount =MAXFLOAT;
// 保持最後的狀態
rotationAnim.removedOnCompletion =NO;
//設置抖動數值
rotationAnim.values =@[@(ANGLE_TO_RADIAN(0)),@(ANGLE_TO_RADIAN(0)),@(ANGLE_TO_RADIAN(-7)),@(ANGLE_TO_RADIAN(7)),@(ANGLE_TO_RADIAN(0)),@(ANGLE_TO_RADIAN(0))];
rotationAnim.keyTimes = @[@(0), @(0.4), @(0.47), @(0.52), @(0.6), @(1.0)];
//動畫的填充模式
rotationAnim.fillMode =kCAFillModeForwards;
//layer層實現動畫
[self.searchLayer addAnimation:rotationAnim forKey:@"shake"];
}
縮放動畫
- (void)scaleAnimation {
CAKeyframeAnimation *scaleAnim = [CAKeyframeAnimation animationWithKeyPath:@"transform.scale"];
scaleAnim.repeatCount = MAXFLOAT;
scaleAnim.duration = 3.0f;
scaleAnim.removedOnCompletion = NO;
scaleAnim.values = @[@(1.0),@(1.0), @(1.2), @(1.2),@(1.0),@(1.0)];
scaleAnim.keyTimes = @[@(0), @(0.2), @(0.3), @(0.7), @(0.8), @(1.0)];
scaleAnim.timingFunctions = @[[CAMediaTimingFunction functionWithName:kCAMediaTimingFunctionLinear], [CAMediaTimingFunction functionWithName:kCAMediaTimingFunctionLinear], [CAMediaTimingFunction functionWithName:kCAMediaTimingFunctionLinear], [CAMediaTimingFunction functionWithName:kCAMediaTimingFunctionLinear]];
[self.searchLayer addAnimation:scaleAnim forKey:@"bouce"];
}
整體實現
分別實現之後,用動畫組將他們組合起來。
效果上顯示字體放大後執行旋轉動畫,這裏我採用關鍵幀動畫的keyTimes
來協調的。因爲Apple並不建議在一個動畫completion回調裏執行另一個動畫。
PS:
當這個button綁定的點擊方法需要跳轉VC時,可以設置動畫組的 removedOnCompletion 屬性爲 NO 來讓 VC 切換不停止動畫。
//繼承自UIButton
@interface ShakeButton : UIButton
@end
//
// ShakeButton.m
// HtmlLoad
//
// Created by zchao on 2018/3/21.
// Copyright © 2018年 zchao. All rights reserved.
//
#import "ShakeButton.h"
#define BoardWidth 2.0f
#define margin 8.0f
#define ANGLE_TO_RADIAN(angle) ((angle)/180.0 * M_PI)
@interface ShakeButton ()
@property(nonatomic, strong) CALayer *searchLayer;
@end
@implementation ShakeButton
- (instancetype)initWithFrame:(CGRect)frame
{
self = [super initWithFrame:frame];
if (self) {
//添加圖層到根圖層
CGFloat x = (2-sqrtf(2))*0.25 * frame.size.width + 0.5*sqrtf(2)*BoardWidth;
CGFloat width = sqrt(2)*0.5*frame.size.width - sqrtf(2)*BoardWidth;
[self.layer addSublayer:[self drawSearchLayer:CGRectMake(x, x, width, width)]];
[self startAnimation];
// [self scaleAnimation];
// [self rotationAnimation];
}
return self;
}
- (void)startAnimation {
CAKeyframeAnimation *rotationAnim = [CAKeyframeAnimation animationWithKeyPath:@"transform.rotation"];
// 動畫時間
rotationAnim.duration =3.0;
//無限次重複
rotationAnim.repeatCount =MAXFLOAT;
// 保持最後的狀態
rotationAnim.removedOnCompletion =NO;
//設置抖動數值
rotationAnim.values =@[@(ANGLE_TO_RADIAN(0)),@(ANGLE_TO_RADIAN(0)),@(ANGLE_TO_RADIAN(-7)),@(ANGLE_TO_RADIAN(7)),@(ANGLE_TO_RADIAN(0)),@(ANGLE_TO_RADIAN(0))];
rotationAnim.keyTimes = @[@(0), @(0.4), @(0.47), @(0.52), @(0.6), @(1.0)];
//動畫的填充模式
rotationAnim.fillMode =kCAFillModeForwards;
CAKeyframeAnimation *scaleAnim = [CAKeyframeAnimation animationWithKeyPath:@"transform.scale"];
scaleAnim.repeatCount = MAXFLOAT;
scaleAnim.duration = 3.0f;
scaleAnim.removedOnCompletion = NO;
scaleAnim.values = @[@(1.0),@(1.0), @(1.2), @(1.2),@(1.0),@(1.0)];
scaleAnim.keyTimes = @[@(0), @(0.2), @(0.3), @(0.7), @(0.8), @(1.0)];
scaleAnim.timingFunctions = @[[CAMediaTimingFunction functionWithName:kCAMediaTimingFunctionLinear], [CAMediaTimingFunction functionWithName:kCAMediaTimingFunctionLinear], [CAMediaTimingFunction functionWithName:kCAMediaTimingFunctionLinear], [CAMediaTimingFunction functionWithName:kCAMediaTimingFunctionLinear]];
CAAnimationGroup *group = [CAAnimationGroup animation];
group.duration = 3.0f;
group.repeatCount = MAXFLOAT;
group.animations = @[rotationAnim, scaleAnim];
group.removedOnCompletion = NO;
[self.searchLayer addAnimation:group forKey:@"fuck"];
}
- (void)scaleAnimation {
CAKeyframeAnimation *scaleAnim = [CAKeyframeAnimation animationWithKeyPath:@"transform.scale"];
scaleAnim.repeatCount = MAXFLOAT;
scaleAnim.duration = 3.0f;
scaleAnim.removedOnCompletion = NO;
scaleAnim.values = @[@(1.0),@(1.0), @(1.2), @(1.2),@(1.0),@(1.0)];
scaleAnim.keyTimes = @[@(0), @(0.2), @(0.3), @(0.7), @(0.8), @(1.0)];
scaleAnim.timingFunctions = @[[CAMediaTimingFunction functionWithName:kCAMediaTimingFunctionLinear], [CAMediaTimingFunction functionWithName:kCAMediaTimingFunctionLinear], [CAMediaTimingFunction functionWithName:kCAMediaTimingFunctionLinear], [CAMediaTimingFunction functionWithName:kCAMediaTimingFunctionLinear]];
[self.searchLayer addAnimation:scaleAnim forKey:@"bouce"];
}
- (void)rotationAnimation {
CAKeyframeAnimation *rotationAnim = [CAKeyframeAnimation animationWithKeyPath:@"transform.rotation"];
// 動畫時間
rotationAnim.duration =3.0;
//無限次重複
rotationAnim.repeatCount =MAXFLOAT;
// 保持最後的狀態
rotationAnim.removedOnCompletion =NO;
//設置抖動數值
rotationAnim.values =@[@(ANGLE_TO_RADIAN(0)),@(ANGLE_TO_RADIAN(0)),@(ANGLE_TO_RADIAN(-7)),@(ANGLE_TO_RADIAN(7)),@(ANGLE_TO_RADIAN(0)),@(ANGLE_TO_RADIAN(0))];
rotationAnim.keyTimes = @[@(0), @(0.4), @(0.47), @(0.52), @(0.6), @(1.0)];
//動畫的填充模式
rotationAnim.fillMode =kCAFillModeForwards;
//layer層實現動畫
[self.searchLayer addAnimation:rotationAnim forKey:@"shake"];
}
//Only override drawRect: if you perform custom drawing. An empty implementation adversely affects performance during animation.
- (void)drawRect:(CGRect)rect {
//Drawing code
CGContextRef context = UIGraphicsGetCurrentContext();
CGContextAddArc(context, rect.size.width/2, rect.size.height/2, rect.size.width/2-BoardWidth, 0, 2*M_PI, 0);
CGPoint aPoints[2];
aPoints[0] =CGPointMake(rect.size.width*(0.5+sqrtf(2)/4), rect.size.width*(0.5+sqrtf(2)/4));
aPoints[1] =CGPointMake(rect.size.width, rect.size.height);
CGContextAddLines(context, aPoints, 2);
//畫筆顏色
CGContextSetRGBStrokeColor(context, 1, 1, 1, 1.0);
//線條粗細
CGContextSetLineWidth(context, BoardWidth);
CGContextDrawPath(context, kCGPathStroke);
}
- (CALayer *)drawSearchLayer:(CGRect)rect {
//自定義圖層
CATextLayer *layer = [[CATextLayer alloc] init];
self.searchLayer = layer;
layer.frame = rect;
layer.foregroundColor = RGBACOLOR(255, 184, 62, 1).CGColor;
layer.string = @"搜";
UIFont *font = [UIFont boldSystemFontOfSize:11];
CFStringRef fontName = (__bridge CFStringRef)font.fontName;
CGFontRef fontRef =CGFontCreateWithFontName(fontName);
layer.font = fontRef;
layer.fontSize = font.pointSize;
CGFontRelease(fontRef);
layer.contentsScale = [UIScreen mainScreen].scale;
layer.alignmentMode = kCAAlignmentCenter;
return layer;
}
@end