A-關(guān)鍵幀動畫
關(guān)鍵幀動畫就是在動畫控制過程中開發(fā)者指定主要的動畫狀態(tài),各個狀態(tài)間動畫如何進(jìn)行則由系統(tǒng)自動運(yùn)算補(bǔ)充(每兩個關(guān)鍵幀之間系統(tǒng)形成的動畫稱為“補(bǔ)間動畫”)宠哄,好處就是開不用逐個控制每個動畫幀乌妒,只要關(guān)心幾個關(guān)鍵幀的狀態(tài)即可礁凡。
關(guān)鍵幀動畫開發(fā)分為兩種形式:
- 通過設(shè)置不同的屬性值(
property
)進(jìn)行關(guān)鍵幀控制, - 通過繪制路徑(
path
)進(jìn)行關(guān)鍵幀控制偿洁。
2的優(yōu)先級高于1涯肩,如果設(shè)置了路徑則屬性值就不再起作用轿钠。
#pragma mark - 關(guān)鍵幀動畫
-(void)translationAnimation{
//1.創(chuàng)建關(guān)鍵幀動畫并設(shè)置動畫屬性
CAKeyframeAnimation *keyframeAnimation=[CAKeyframeAnimation animationWithKeyPath:@"position"];
//2.設(shè)置關(guān)鍵幀,這里有四個關(guān)鍵幀(使用property)
NSValue *key1=[NSValue valueWithCGPoint:_layer.position];//對于關(guān)鍵幀動畫初始值不能省略
NSValue *key2=[NSValue valueWithCGPoint:CGPointMake(80, 220)];
NSValue *key3=[NSValue valueWithCGPoint:CGPointMake(45, 300)];
NSValue *key4=[NSValue valueWithCGPoint:CGPointMake(55, 400)];
NSArray *values=@[key1,key2,key3,key4];
keyframeAnimation.values=values;
//各個關(guān)鍵幀的時間控制
/*默認(rèn)情況下每兩幀之間的間隔為:8/(4-1)秒。
如果要控制動畫從第一幀到第二針占用時間4秒病苗,
從第二幀到第三幀時間為2秒疗垛,從第三幀到第四幀時間2秒的話,
就可以通過keyTimes進(jìn)行設(shè)置铅乡。keyTimes中存儲的是時間占用比例點(diǎn)继谚,
設(shè)置keyTimes的值為0.0,0.5阵幸,0.75,1.0(當(dāng)然必須轉(zhuǎn)換為NSNumber)芽世,
也就是1到2幀運(yùn)行到總時間的50%挚赊,2到3幀運(yùn)行到75%,3到4幀運(yùn)行到8秒結(jié)束济瓢。*/
keyframeAnimation.keyTimes = @[@0.0,@0.5,@0.7,@1.0];
/* 2.設(shè)置路徑(使用path)
//繪制貝塞爾曲線
CGPathRef path=CGPathCreateMutable();
CGPathMoveToPoint(path, NULL, _layer.position.x, _layer.position.y);//移動到起始點(diǎn)
CGPathAddCurveToPoint(path, NULL, 160, 280, -30, 300, 55, 400);//繪制二次貝塞爾曲線
keyframeAnimation.path=path;//設(shè)置path屬性
CGPathRelease(path);//釋放路徑對象*/
//設(shè)置其他屬性
keyframeAnimation.duration=8.0;
keyframeAnimation.beginTime=CACurrentMediaTime()+2;//設(shè)置延遲2秒執(zhí)行
//3.添加動畫到圖層荠割,添加動畫后就會執(zhí)行動畫
[_layer addAnimation:keyframeAnimation forKey:@"XDKeyframeAnimation_Position"];
}
caculationMode
:動畫計算模式。之所以1到2幀能形成連貫性動畫而不是直接從第1幀經(jīng)過8/3秒到第2幀是因為動畫模式是連續(xù)的(值為kCAAnimationLinear
旺矾,這是計算模式的默認(rèn)值)蔑鹦;而如果指定為kCAAnimationDiscrete
離散的那么動畫從第1幀經(jīng)過8/3秒直接到第2幀,中間沒有任何過渡箕宙。其他動畫模式還有:kCAAnimationPaced
(均勻執(zhí)行嚎朽,會忽略keyTimes)、kCAAnimationCubic
(平滑執(zhí)行柬帕,對于位置變動關(guān)鍵幀動畫運(yùn)行軌跡更平滑)哟忍、kCAAnimationCubicPaced
(平滑均勻執(zhí)行)。
下圖描繪出了幾種動畫模式的關(guān)系(橫坐標(biāo)是運(yùn)行時間陷寝,縱坐標(biāo)是動畫屬性[例如位置锅很、透明度等]):
B-組合動畫
#pragma mark 基礎(chǔ)旋轉(zhuǎn)動畫
-(CABasicAnimation *)rotationAnimation{
CABasicAnimation *basicAnimation=[CABasicAnimation animationWithKeyPath:@"transform.rotation.z"];
CGFloat toValue=M_PI_2*3;
basicAnimation.toValue=[NSNumber numberWithFloat:M_PI_2*3];
// basicAnimation.duration=6.0;
basicAnimation.autoreverses=true;
basicAnimation.repeatCount=HUGE_VALF;
basicAnimation.removedOnCompletion=NO;
[basicAnimation setValue:[NSNumber numberWithFloat:toValue] forKey:@"KCBasicAnimationProperty_ToValue"];
return basicAnimation;
}
#pragma mark 關(guān)鍵幀移動動畫
-(CAKeyframeAnimation *)translationAnimation{
CAKeyframeAnimation *keyframeAnimation=[CAKeyframeAnimation animationWithKeyPath:@"position"];
CGPoint endPoint= CGPointMake(55, 400);
CGPathRef path=CGPathCreateMutable();
CGPathMoveToPoint(path, NULL, _layer.position.x, _layer.position.y);
CGPathAddCurveToPoint(path, NULL, 160, 280, -30, 300, endPoint.x, endPoint.y);
keyframeAnimation.path=path;
CGPathRelease(path);
[keyframeAnimation setValue:[NSValue valueWithCGPoint:endPoint] forKey:@"KCKeyframeAnimationProperty_EndPosition"];
return keyframeAnimation;
}
#pragma mark 創(chuàng)建動畫組
-(void)groupAnimation{
//1.創(chuàng)建動畫組
CAAnimationGroup *animationGroup=[CAAnimationGroup animation];
//2.設(shè)置組中的動畫和其他屬性
CABasicAnimation *basicAnimation=[self rotationAnimation];
CAKeyframeAnimation *keyframeAnimation=[self translationAnimation];
animationGroup.animations=@[basicAnimation,keyframeAnimation];
animationGroup.delegate=self;
animationGroup.duration=10.0;//設(shè)置動畫時間,如果動畫組中動畫已經(jīng)設(shè)置過動畫屬性則不再生效
animationGroup.beginTime=CACurrentMediaTime()+5;//延遲五秒執(zhí)行
//3.給圖層添加動畫
[_layer addAnimation:animationGroup forKey:nil];
}
#pragma mark - 代理方法
#pragma mark 動畫完成
-(void)animationDidStop:(CAAnimation *)anim finished:(BOOL)flag{
CAAnimationGroup *animationGroup=(CAAnimationGroup *)anim;
CABasicAnimation *basicAnimation=animationGroup.animations[0];
CAKeyframeAnimation *keyframeAnimation=animationGroup.animations[1];
CGFloat toValue=[[basicAnimation valueForKey:@"KCBasicAnimationProperty_ToValue"] floatValue];
CGPoint endPoint=[[keyframeAnimation valueForKey:@"KCKeyframeAnimationProperty_EndPosition"] CGPointValue];
[CATransaction begin];
[CATransaction setDisableActions:YES];
//設(shè)置動畫最終狀態(tài)
_layer.position=endPoint;
_layer.transform=CATransform3DMakeRotation(toValue, 0, 0, 1);
[CATransaction commit];
}
C-轉(zhuǎn)場動畫
轉(zhuǎn)場動畫
就是從一個場景以動畫的形式過渡到另一個場景凤跑。
- 創(chuàng)建轉(zhuǎn)場動畫
- 設(shè)置轉(zhuǎn)場類型,子類型(可選)以及其他屬性
- 設(shè)置轉(zhuǎn)場后的新視圖并添加動畫到圖層
轉(zhuǎn)場類型:
-
fade
淡出效果(kCATransitionFade
) -
movein
新視圖移動到舊視圖上(kCATransitionMoveIn
) -
push
新視圖推出舊視圖(kCATransitionPush
) -
reveal
移開舊視圖顯示新視圖(kCATransitionReveal
)
[私有API效果:cube
(立方體翻轉(zhuǎn)效果),oglFlip
(翻轉(zhuǎn)效果),suckEffect
(收縮效果),rippleEffect
(水滴波紋效果),pageCurl
(向上翻頁效果),pageUnCurl
(向下翻頁效果),cameralIrisHollowOpen
(攝像頭打開效果),cameraIrisHollowClose
(攝像頭關(guān)閉效果)]
對于支持方向設(shè)置的動畫類型還包含子類型:
-
kCATransitionFromRight
從右側(cè)轉(zhuǎn)場 -
kCATransitionFromLeft
從左側(cè)轉(zhuǎn)場 -
kCATransitionFromTop
從頂部轉(zhuǎn)場 -
kCATransitionFromBottom
從底部轉(zhuǎn)場
//
// ThirdViewController.m
// CartoonLearn
//
// Created by xiaodoubaba on 16/2/18.
// Copyright ? 2016年 xiaodoubaba. All rights reserved.
//
#import "ThirdViewController.h"
#define ImageCount 6
@interface ThirdViewController ()
@property (strong, nonatomic) UIImageView *imgView;
@property (assign, nonatomic) int curIndex;
@end
@implementation ThirdViewController
- (void)viewDidLoad {
[super viewDidLoad];
[self.view addSubview:self.imgView];
UISwipeGestureRecognizer *leftSwipeGesture = [[UISwipeGestureRecognizer alloc] initWithTarget:self action:@selector(leftSwipe:)];
leftSwipeGesture.direction = UISwipeGestureRecognizerDirectionLeft;
[self.view addGestureRecognizer:leftSwipeGesture];
UISwipeGestureRecognizer *rightSwipeGesture = [[UISwipeGestureRecognizer alloc] initWithTarget:self action:@selector(rightSwipe:)];
rightSwipeGesture.direction = UISwipeGestureRecognizerDirectionRight;
[self.view addGestureRecognizer:rightSwipeGesture];
}
#pragma mark - gesture
- (void)leftSwipe:(UISwipeGestureRecognizer *)gesture
{
//其實(shí)就是向下切換圖片
[self transitionAnimation:YES];
}
- (void)rightSwipe:(UISwipeGestureRecognizer *)gesture
{
//其實(shí)就是向上切換動畫
[self transitionAnimation:NO];
}
#pragma mark - transitionAnimation
- (void)transitionAnimation:(BOOL)isNext
{
//1.創(chuàng)建轉(zhuǎn)場動畫對象
CATransition *transition = [CATransition new];
//2.設(shè)置動畫類型
transition.type = @"cube";
//設(shè)置子類型
if (isNext) {
transition.subtype = kCATransitionFromRight;
}else{
transition.subtype = kCATransitionFromLeft;
}
//設(shè)置動畫時常
transition.duration = 1.0f;
//3.設(shè)置轉(zhuǎn)場后的新視圖并且添加轉(zhuǎn)場動畫(動畫其實(shí)就是切換圖片動畫)
self.imgView.image = [self getImg:isNext];
[self.imgView.layer addAnimation:transition forKey:@"KCTransitionAnimation"];
}
- (UIImage *)getImg:(BOOL)isNext
{
if (isNext) {
_curIndex = (_curIndex + 1) % ImageCount;
}else{
_curIndex = (_curIndex - 1 + ImageCount) % ImageCount;
}
NSString *imgName = [NSString stringWithFormat:@"%d",_curIndex];
return [UIImage imageNamed:imgName];
}
#pragma mark - getter
- (UIImageView *)imgView
{
if (!_imgView) {
_imgView = [UIImageView new];
_imgView.frame = [UIScreen mainScreen].applicationFrame;
_imgView.contentMode = UIViewContentModeScaleAspectFill;
_imgView.image = [UIImage imageNamed:@"0"];
}
return _imgView;
}
@end
D-逐幀動畫
通過設(shè)置UIImageView
的animationImages
屬性爆安,然后調(diào)用它的startAnimating
方法去播放這組圖片,這就是一個逐幀動畫仔引。但是它一次性加載大量圖片扔仓,性能存在問題褐奥,并且中間過程無法控制。(如果iOS
的定時器NSTimer
定時更新圖片來達(dá)到逐幀動畫的效果当辐,缺點(diǎn)就是定時器方法調(diào)用有時可能會因為當(dāng)前系統(tǒng)執(zhí)行某種比較占用時間的任務(wù)造成動畫連續(xù)性出現(xiàn)問題抖僵。)
CADisplayLink
是一個計時器,和NSTimer
不同的是缘揪,CADisplayLink
的刷新周期同屏幕完全一致耍群。例如在iOS
中屏幕刷新周期是60次/秒,CADisplayLink
刷新周期同屏幕刷新一致也是60次/秒找筝,這樣一來使用它完成的逐幀動畫(又稱為“時鐘動畫”)完全感覺不到動畫的停滯情況蹈垢。
#import "ViewController.h"
#define IMAGE_COUNT 10
@interface ViewController (){
CALayer *_layer;
int _index;
NSMutableArray *_images;
}
@end
@implementation ViewController
- (void)viewDidLoad {
[super viewDidLoad];
//設(shè)置背景
self.view.layer.contents=(id)[UIImage imageNamed:@"bg.png"].CGImage;
//創(chuàng)建圖像顯示圖層
_layer=[[CALayer alloc]init];
_layer.bounds=CGRectMake(0, 0, 87, 32);
_layer.position=CGPointMake(160, 284);
[self.view.layer addSublayer:_layer];
//由于魚的圖片在循環(huán)中會不斷創(chuàng)建,而10張魚的照片相對都很小
//與其在循環(huán)中不斷創(chuàng)建UIImage不如直接將10張圖片緩存起來
_images=[NSMutableArray array];
for (int i=0; i<10; ++i) {
NSString *imageName=[NSString stringWithFormat:@"fish%i.png",i];
UIImage *image=[UIImage imageNamed:imageName];
[_images addObject:image];
}
//定義時鐘對象
CADisplayLink *displayLink=[CADisplayLink displayLinkWithTarget:self selector:@selector(step)];
//添加時鐘對象到主運(yùn)行循環(huán)
[displayLink addToRunLoop:[NSRunLoop mainRunLoop] forMode:NSDefaultRunLoopMode];
}
#pragma mark 每次屏幕刷新就會執(zhí)行一次此方法(每秒接近60次)
-(void)step{
//定義一個變量記錄執(zhí)行次數(shù)
static int s=0;
//每秒執(zhí)行6次
if (++s%10==0) {
UIImage *image=_images[_index];
_layer.contents=(id)image.CGImage;//更新圖片
_index=(_index+1)%IMAGE_COUNT;
}
}
@end