IOS動畫學(xué)習(xí)小記(4)-關(guān)鍵幀動畫,組合動畫,轉(zhuǎn)場動畫

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ā)分為兩種形式:

  1. 通過設(shè)置不同的屬性值(property)進(jìn)行關(guān)鍵幀控制,
  2. 通過繪制路徑(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)是動畫屬性[例如位置锅很、透明度等]):

動畫計算模式.png
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)場動畫就是從一個場景以動畫的形式過渡到另一個場景凤跑。

  1. 創(chuàng)建轉(zhuǎn)場動畫
  2. 設(shè)置轉(zhuǎn)場類型,子類型(可選)以及其他屬性
  3. 設(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
轉(zhuǎn)場動畫.png
D-逐幀動畫

通過設(shè)置UIImageViewanimationImages屬性爆安,然后調(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
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末袖裕,一起剝皮案震驚了整個濱河市曹抬,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌急鳄,老刑警劉巖谤民,帶你破解...
    沈念sama閱讀 211,265評論 6 490
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異疾宏,居然都是意外死亡张足,警方通過查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 90,078評論 2 385
  • 文/潘曉璐 我一進(jìn)店門坎藐,熙熙樓的掌柜王于貴愁眉苦臉地迎上來为牍,“玉大人,你說我怎么就攤上這事岩馍〉锱兀” “怎么了?”我有些...
    開封第一講書人閱讀 156,852評論 0 347
  • 文/不壞的土叔 我叫張陵蛀恩,是天一觀的道長疫铜。 經(jīng)常有香客問我,道長赦肋,這世上最難降的妖魔是什么块攒? 我笑而不...
    開封第一講書人閱讀 56,408評論 1 283
  • 正文 為了忘掉前任,我火速辦了婚禮佃乘,結(jié)果婚禮上囱井,老公的妹妹穿的比我還像新娘。我一直安慰自己趣避,他們只是感情好庞呕,可當(dāng)我...
    茶點(diǎn)故事閱讀 65,445評論 5 384
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著,像睡著了一般住练。 火紅的嫁衣襯著肌膚如雪地啰。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 49,772評論 1 290
  • 那天讲逛,我揣著相機(jī)與錄音亏吝,去河邊找鬼。 笑死盏混,一個胖子當(dāng)著我的面吹牛蔚鸥,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播许赃,決...
    沈念sama閱讀 38,921評論 3 406
  • 文/蒼蘭香墨 我猛地睜開眼止喷,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了混聊?” 一聲冷哼從身側(cè)響起弹谁,我...
    開封第一講書人閱讀 37,688評論 0 266
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎句喜,沒想到半個月后预愤,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 44,130評論 1 303
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡咳胃,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 36,467評論 2 325
  • 正文 我和宋清朗相戀三年鳖粟,在試婚紗的時候發(fā)現(xiàn)自己被綠了。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片拙绊。...
    茶點(diǎn)故事閱讀 38,617評論 1 340
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖泳秀,靈堂內(nèi)的尸體忽然破棺而出标沪,到底是詐尸還是另有隱情,我是刑警寧澤嗜傅,帶...
    沈念sama閱讀 34,276評論 4 329
  • 正文 年R本政府宣布金句,位于F島的核電站,受9級特大地震影響吕嘀,放射性物質(zhì)發(fā)生泄漏违寞。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 39,882評論 3 312
  • 文/蒙蒙 一偶房、第九天 我趴在偏房一處隱蔽的房頂上張望趁曼。 院中可真熱鬧,春花似錦棕洋、人聲如沸挡闰。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,740評論 0 21
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽摄悯。三九已至赞季,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間奢驯,已是汗流浹背申钩。 一陣腳步聲響...
    開封第一講書人閱讀 31,967評論 1 265
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留瘪阁,地道東北人撒遣。 一個月前我還...
    沈念sama閱讀 46,315評論 2 360
  • 正文 我出身青樓,卻偏偏與公主長得像罗洗,于是被迫代替她去往敵國和親愉舔。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 43,486評論 2 348

推薦閱讀更多精彩內(nèi)容