前言
來源于raywenderlich
本文可以看做是入門CoreAnimation的一片文章工禾。其中你會學(xué)習(xí)到
CABasicAnimation
CAAnimationGroup
CAKeyframeAnimation
CASpringAnimation
以及部分UIView
動(dòng)畫的使用
先看下最終的效果
-
開始
開始的樣子應(yīng)該是這樣匾荆,你如果覺得閑太麻煩搔确,可以直接使用xib拖拽,位置大致對齊就可以娃惯,因?yàn)槲覀兊闹攸c(diǎn)是學(xué)習(xí)動(dòng)畫
初始樣子 -
接下來是我們第一個(gè)動(dòng)畫---飛入
飛入效果是讓標(biāo)題和用戶名朝巫、密碼的輸入框依次從左邊飛入到屏幕中間
首先我們要把這三個(gè)控件的橫坐標(biāo)都往左移除一個(gè)屏幕的寬度。
飛入.png
CABasicAnimation *flyRightAnimation = [CABasicAnimation animationWithKeyPath:@"position.x"];
flyRightAnimation.delegate = self;
[flyRightAnimation setValue:@"form" forKey:@"name"];
[flyRightAnimation setValue:self.headingLabel.layer forKey:@"layer"];
flyRightAnimation.toValue = [NSValue valueWithCGPoint: CGPointMake(MainSize.width * 0.5, 0)];
flyRightAnimation.fromValue = [NSValue valueWithCGPoint: CGPointMake(-MainSize.width * 0.5, 0)];//-@(MainSize.width * 0.5);
flyRightAnimation.duration = .5;
flyRightAnimation.fillMode = kCAFillModeBoth;
[self.headingLabel.layer addAnimation:flyRightAnimation forKey:nil];
flyRightAnimation.beginTime = CACurrentMediaTime() + 0.3;
[flyRightAnimation setValue:self.userNameTextField.layer forKey:@"layer"];
[self.userNameTextField.layer addAnimation:flyRightAnimation forKey:nil];
flyRightAnimation.beginTime = CACurrentMediaTime() + 0.4;
[flyRightAnimation setValue:self.passWordTextField.layer forKey:@"layer"];
[self.passWordTextField.layer addAnimation:flyRightAnimation forKey:nil];
通過KeyPath設(shè)置屬性動(dòng)畫還可以是position, bounds, transform 等等石景,設(shè)置了key、value是方便待會動(dòng)畫結(jié)束后的后序動(dòng)畫拙吉,簡單說下屬性:
-
beginTime
:設(shè)置動(dòng)畫開始的絕對時(shí)間潮孽,所以我們要先獲取當(dāng)前時(shí)間然后再添加所需的延遲時(shí)間
-
fillMode
:這個(gè)屬性控制你動(dòng)畫隊(duì)列的開始和結(jié)束的行為
-
kCAFillModeRemoved
是默認(rèn)屬性,他會在動(dòng)畫完成的時(shí)候清除動(dòng)畫做的改變
kCAFillModeRemoved -
kCAFillModeBackwards
會顯示第一幀筷黔,不管你的動(dòng)畫開始時(shí)間往史,然后在進(jìn)行動(dòng)畫
kCAFillModeBackwards.png -
kCAFillModeForwards
這是最常用的一種,他會保持動(dòng)畫的最后一幀知道你移除動(dòng)畫佛舱。kCAFillModeForwards.png -
kCAFillModeBoth
這是組合了kCAFillModeForwards和kCAFillModeBackwards椎例,你會馬上看到第一幀挨决,當(dāng)動(dòng)畫結(jié)束后悔顯示最后一幀
Snip20170418_6.png - 脈沖動(dòng)畫
接著剛才我們已經(jīng)把標(biāo)題和輸入框都相繼飛入屏幕中,會有一個(gè)變大的效果订歪。首先我們要等待動(dòng)畫結(jié)束脖祈,因?yàn)槲覀儎偛旁谑褂脛?dòng)畫的時(shí)候已經(jīng)設(shè)置了代理,所以我們只需要在動(dòng)畫結(jié)束的回調(diào)函數(shù)- (void)animationDidStop:(CAAnimation *)anim finished:(BOOL)flag;
中添加變大效果就可以刷晋。這里我們使用核心動(dòng)畫給的CASpringAnimation
NSString *name = [anim valueForKey:@"name"];
if ([name isEqualToString:@"form"]) {
CALayer *layer = [anim valueForKey:@"layer"];
[anim setValue:nil forKey:@"layer"];
//脈沖動(dòng)畫
CASpringAnimation *pulseAnimation = [CASpringAnimation animationWithKeyPath:@"transform.scale"];
pulseAnimation.damping = 7.5;
pulseAnimation.fromValue = @(1.25);
pulseAnimation.toValue = @(1.);
pulseAnimation.duration = pulseAnimation.settlingDuration;
[layer addAnimation:pulseAnimation forKey:nil];
}
先簡單說下CASpringAnimation
的一些屬性:
damping
:阻尼和彈簧一樣盖高,在其他因素不變的情況下阻尼越大回復(fù)形變越快
mass
:質(zhì)量,在胡克定律中沒有質(zhì)量這個(gè)變量,但是在有阻尼的情況下質(zhì)量會影響彈簧的收縮快慢等眼虱。
stiffness
:彈簧的剛度與重量
initialVelocity
:初始速度
效果:
- 云彩動(dòng)畫
在標(biāo)題和輸入框都相繼飛入屏幕中的時(shí)候我們需要把背景上的云朵都一個(gè)一個(gè)的顯示出來
//cloud
CABasicAnimation *fadeAnimation = [CABasicAnimation animationWithKeyPath:@"opacity"];
fadeAnimation.fromValue = @0.;
fadeAnimation.toValue = @1.;
fadeAnimation.duration = .5;
fadeAnimation.fillMode = kCAFillModeBackwards;
fadeAnimation.beginTime = CACurrentMediaTime() + .5;
[self.cloud1ImageV.layer addAnimation:fadeAnimation forKey:nil];
fadeAnimation.beginTime = CACurrentMediaTime() + .7;
[self.cloud2ImageV.layer addAnimation:fadeAnimation forKey:nil];
fadeAnimation.beginTime = CACurrentMediaTime() + .9;
[self.cloud3ImageV.layer addAnimation:fadeAnimation forKey:nil];
fadeAnimation.beginTime = CACurrentMediaTime() + 1.1;
[self.cloud4ImageV.layer addAnimation:fadeAnimation forKey:nil];
云彩出來了喻奥,還需要讓他們不斷循環(huán)的往右移動(dòng)
CABasicAnimation *moveAnimation = [CABasicAnimation animationWithKeyPath:@"position.x"];
[moveAnimation setValue:@"cloud" forKey:@"name"];
[moveAnimation setValue:layer forKey:@"layer"];
moveAnimation.delegate = self;
moveAnimation.duration = duration;
moveAnimation.fillMode = kCAFillModeForwards;
moveAnimation.fromValue = @(layer.position.x);
moveAnimation.toValue = @(self.view.ai_width + layer.bounds.size.width * 0.5);
[layer addAnimation:moveAnimation forKey:nil];
layer.position = CGPointMake(-layer.bounds.size.width/2, layer.position.y);
然后在代理- (void)animationDidStop:(CAAnimation *)anim finished:(BOOL)flag
中判斷key,讓他回到屏幕左邊繼續(xù)像右運(yùn)動(dòng)
if ([name isEqualToString:@"cloud"]) {
CALayer *layer = [anim valueForKey:@"layer"];
layer.position = CGPointMake(-layer.bounds.size.width/2, layer.position.y);
[self animationCloud:layer];
}
這里需要注意的是捏悬,這兩個(gè)函數(shù)你調(diào)用我撞蚕,我調(diào)用你會形成死循環(huán),導(dǎo)致內(nèi)存泄漏过牙,所以在做動(dòng)畫的前面加一個(gè)全局變量房租內(nèi)存泄漏
if (!self.isAppear) {//如果沒有顯示直接return防止內(nèi)存泄漏
return;
}
- 登錄按鈕動(dòng)畫
接下來是組合動(dòng)畫CAAnimationGroup
組合動(dòng)畫你可以調(diào)整整體的屬性甥厦,如:持續(xù)時(shí)間、代理抒和、動(dòng)畫節(jié)奏等矫渔。
//loginBtn
CAAnimationGroup *groupAnimation = [[CAAnimationGroup alloc]init];
groupAnimation.beginTime = CACurrentMediaTime() + .5;
groupAnimation.duration = .5;
groupAnimation.fillMode = kCAFillModeBackwards;
groupAnimation.timingFunction = [CAMediaTimingFunction functionWithName:kCAMediaTimingFunctionEaseIn];
CABasicAnimation *scaleDownAnimation = [CABasicAnimation animationWithKeyPath:@"transform.scale"];
scaleDownAnimation.fromValue = @3.5;
scaleDownAnimation.toValue = @1.;
CABasicAnimation *rotateAnimation = [CABasicAnimation animationWithKeyPath:@"transform.rotation"];
rotateAnimation.fromValue = @M_PI_4;
rotateAnimation.toValue = @0;
CABasicAnimation *fadeAnimation = [CABasicAnimation animationWithKeyPath:@"opacity"];
fadeAnimation.fromValue = @0.;
fadeAnimation.toValue = @1.;
groupAnimation.animations = @[fadeAnimation,scaleDownAnimation,rotateAnimation];
[self.loginBtn.layer addAnimation:groupAnimation forKey:nil];
self.loginBtn.alpha = 1;
timingFunction
:你可以理解為動(dòng)畫的節(jié)奏,下面幾個(gè)常見的值
-
kCAMediaTimingFunctionLinear
:以勻速進(jìn)行整個(gè)動(dòng)畫 -
kCAMediaTimingFunctionEaseIn
:一開始慢然后快
kCAMediaTimingFunctionEaseIn -
kCAMediaTimingFunctionEaseOut
:一開始快然后慢
kCAMediaTimingFunctionEaseOut
-
點(diǎn)擊登錄動(dòng)畫
點(diǎn)擊登錄按鈕的時(shí)候登錄按鈕摧莽,需要往下移動(dòng)庙洼,然后變顏色,以及圓角變大镊辕,寬度變寬油够,顯示指示器(也就是菊花)。
點(diǎn)擊登錄
//彈簧動(dòng)畫變寬
AIWeakSelf
[UIView animateWithDuration:1.5 delay:0. usingSpringWithDamping:.2 initialSpringVelocity:0. options:(UIViewAnimationOptionCurveLinear) animations:^{
CGRect loginBounds = self.loginBtn.bounds;
loginBounds.size.width += 80;
weakSelf.loginBtn.bounds = loginBounds;
} completion:^(BOOL finished) {
[weakSelf showMessageWithIndex:0];
}];
//spinner
[UIView animateWithDuration:.33 delay:0 usingSpringWithDamping:.7 initialSpringVelocity:0 options:(UIViewAnimationOptionCurveLinear) animations:^{
weakSelf.loginBtn.ai_centerY += 60;
weakSelf.spinner.ai_x = 40;
weakSelf.spinner.alpha = 1;
weakSelf.spinner.ai_centerY = weakSelf.loginBtn.ai_middleY;
} completion:nil];
這里直接使用UIView動(dòng)畫
duration
:動(dòng)畫持續(xù)時(shí)間
delay
:動(dòng)畫延遲時(shí)間
dampingRatio
:彈簧阻尼
velocity
:彈簧速度
options
:這里可以選擇動(dòng)畫的節(jié)奏征懈、是否重復(fù)等
animations
:動(dòng)畫的回調(diào)
completion
:動(dòng)畫完成后的回調(diào)
然后是設(shè)置圓角的背景顏色動(dòng)畫
/**
動(dòng)畫來改變layer的背景顏色
@param layer 改變的layer
@param toColor 改變的顏色
*/
- (void)tintBackgroundColorWithCALayer:(CALayer*)layer toColor:(UIColor*)toColor{
CASpringAnimation *tintAnimation = [CASpringAnimation animationWithKeyPath:@"backgroundColor"];
tintAnimation.fromValue = (__bridge id _Nullable)(layer.backgroundColor);
tintAnimation.toValue = (__bridge id _Nullable)(toColor.CGColor);
tintAnimation.duration = tintAnimation.settlingDuration;
tintAnimation.damping = 7.;
tintAnimation.mass = 10.;
[layer addAnimation:tintAnimation forKey:nil];
layer.backgroundColor = toColor.CGColor;
}
/**
設(shè)置圓角動(dòng)畫
@param layer 動(dòng)畫的layer
@param radius 圓角半徑
*/
- (void)roundCornersWithCALayer:(CALayer*)layer toRadius:(CGFloat)radius {
CASpringAnimation *radiusAnimation = [CASpringAnimation animationWithKeyPath:@"cornerRadius"];
radiusAnimation.fromValue = @(layer.cornerRadius);
radiusAnimation.toValue = @(radius);
radiusAnimation.duration = radiusAnimation.settlingDuration;
radiusAnimation.damping = 17.;
[layer addAnimation:radiusAnimation forKey:nil];
layer.cornerRadius = radius;
}
這里都單獨(dú)提出一個(gè)方法來執(zhí)行l(wèi)ayer對應(yīng)的動(dòng)畫石咬,方便以后的項(xiàng)目如果還有這類似的改變顏色或者改變圓角的動(dòng)畫就可以直接把方法復(fù)制過去使用
我們看到中間還會顯示一個(gè)信息條
/**
顯示一條信息
@param index 第幾條
*/
- (void)showMessageWithIndex:(NSInteger)index {
self.label.text = self.messages[index];
AIWeakSelf
[UIView transitionWithView:self.statusImageV duration:.33 options:(UIViewAnimationOptionCurveEaseOut|UIViewAnimationOptionTransitionFlipFromBottom) animations:^{
weakSelf.statusImageV.hidden = NO;
} completion:^(BOOL finished) {
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(2. * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
if (index < (weakSelf.messages.count - 1)) {
[weakSelf removeMessageWithIndex:index];
}else{
[weakSelf resetFrom];
}
});
}];
}
/**
提出一條信息
@param index 第幾條
*/
- (void)removeMessageWithIndex:(NSInteger)index {
AIWeakSelf
[UIView animateWithDuration:.33 animations:^{
weakSelf.statusImageV.ai_centerX += MainSize.width;
} completion:^(BOOL finished) {
weakSelf.statusImageV.hidden = YES;
weakSelf.statusImageV.center = self.statusPoint;
[weakSelf showMessageWithIndex:index+1];
}];
}
當(dāng)幾條信息顯示完了后,我們需要重置登錄按鈕狀態(tài)卖哎,這里有個(gè)標(biāo)題有個(gè)搖晃的效果
這里我們使用
CAKeyframeAnimation
CAKeyframeAnimation *wobbleAniamtion = [CAKeyframeAnimation animationWithKeyPath:@"transform.rotation"];
wobbleAniamtion.duration = 0.25;
wobbleAniamtion.repeatCount = 4;
wobbleAniamtion.values = @[@0.0, @(-M_PI_4), @0.0, @M_PI_4, @0.0];
wobbleAniamtion.keyTimes = @[@0.0, @0.25, @0.5, @0.75, @1.0];
[weakSelf.headingLabel.layer addAnimation:wobbleAniamtion forKey:nil];
以CABasicAnimation一樣的方式創(chuàng)建一個(gè)CAKeyframeAnimation設(shè)置他的重復(fù)次數(shù)鬼悠,持續(xù)是時(shí)間等。
values
:動(dòng)畫的關(guān)鍵點(diǎn)亏娜,選擇角度從0°到-45°再到0°再到45°最后回到0°焕窝。(這里起始和結(jié)束一樣可以讓重復(fù)的動(dòng)畫的時(shí)候看上去很自然)
keyTimes
:關(guān)鍵時(shí)間,一定要確保你的關(guān)鍵時(shí)間是從0開始到1結(jié)束维贺。
還有些細(xì)節(jié)的動(dòng)畫它掂,由于代碼比較多又是比較重復(fù)的技術(shù)就不貼出來了有興趣可以下載源碼
喜歡的在github上給個(gè)star