10.1 動(dòng)畫速度

2018-02-24 15:07 更新

動(dòng)畫速度

動(dòng)畫實(shí)際上就是一段時(shí)間內(nèi)的變化,這就暗示了變化一定是隨著某個(gè)特定的速率進(jìn)行。速率由以下公式計(jì)算而來:

velocity = change / time

這里的變化可以指的是一個(gè)物體移動(dòng)的距離,時(shí)間指動(dòng)畫持續(xù)的時(shí)長,用這樣的一個(gè)移動(dòng)可以更加形象的描述(比如positionbounds屬性的動(dòng)畫),但實(shí)際上它應(yīng)用于任意可以做動(dòng)畫的屬性(比如coloropacity)。

上面的等式假設(shè)了速度在整個(gè)動(dòng)畫過程中都是恒定不變的(就如同第八章“顯式動(dòng)畫”的情況),對(duì)于這種恒定速度的動(dòng)畫我們稱之為“線性步調(diào)”,而且從技術(shù)的角度而言這也是實(shí)現(xiàn)動(dòng)畫最簡單的方式,但也是完全不真實(shí)的一種效果。

考慮一個(gè)場景,一輛車行駛在一定距離內(nèi),它并不會(huì)一開始就以60mph的速度行駛,然后到達(dá)終點(diǎn)后突然變成0mph。一是因?yàn)樾枰獰o限大的加速度(即使是最好的車也不會(huì)在0秒內(nèi)從0跑到60),另外不然的話會(huì)干死所有乘客。在現(xiàn)實(shí)中,它會(huì)慢慢地加速到全速,然后當(dāng)它接近終點(diǎn)的時(shí)候,它會(huì)慢慢地減速,直到最后停下來。

那么對(duì)于一個(gè)掉落到地上的物體又會(huì)怎樣呢?它會(huì)首先停在空中,然后一直加速到落到地面,然后突然停止(然后由于積累的動(dòng)能轉(zhuǎn)換伴隨著一聲巨響,砰?。?。

現(xiàn)實(shí)生活中的任何一個(gè)物體都會(huì)在運(yùn)動(dòng)中加速或者減速。那么我們?nèi)绾卧趧?dòng)畫中實(shí)現(xiàn)這種加速度呢?一種方法是使用物理引擎來對(duì)運(yùn)動(dòng)物體的摩擦和動(dòng)量來建模,然而這會(huì)使得計(jì)算過于復(fù)雜。我們稱這種類型的方程為緩沖函數(shù),幸運(yùn)的是,Core Animation內(nèi)嵌了一系列標(biāo)準(zhǔn)函數(shù)提供給我們使用。

CAMediaTimingFunction

那么該如何使用緩沖方程式呢?首先需要設(shè)置CAAnimationtimingFunction屬性,是CAMediaTimingFunction類的一個(gè)對(duì)象。如果想改變隱式動(dòng)畫的計(jì)時(shí)函數(shù),同樣也可以使用CATransaction+setAnimationTimingFunction:方法。

這里有一些方式來創(chuàng)建CAMediaTimingFunction,最簡單的方式是調(diào)用+timingFunctionWithName:的構(gòu)造方法。這里傳入如下幾個(gè)常量之一:

kCAMediaTimingFunctionLinear 
kCAMediaTimingFunctionEaseIn 
kCAMediaTimingFunctionEaseOut 
kCAMediaTimingFunctionEaseInEaseOut
kCAMediaTimingFunctionDefault

kCAMediaTimingFunctionLinear選項(xiàng)創(chuàng)建了一個(gè)線性的計(jì)時(shí)函數(shù),同樣也是CAAnimationtimingFunction屬性為空時(shí)候的默認(rèn)函數(shù)。線性步調(diào)對(duì)于那些立即加速并且保持勻速到達(dá)終點(diǎn)的場景會(huì)有意義(例如射出槍膛的子彈),但是默認(rèn)來說它看起來很奇怪,因?yàn)閷?duì)大多數(shù)的動(dòng)畫來說確實(shí)很少用到。

kCAMediaTimingFunctionEaseIn常量創(chuàng)建了一個(gè)慢慢加速然后突然停止的方法。對(duì)于之前提到的自由落體的例子來說很適合,或者比如對(duì)準(zhǔn)一個(gè)目標(biāo)的導(dǎo)彈的發(fā)射。

kCAMediaTimingFunctionEaseOut則恰恰相反,它以一個(gè)全速開始,然后慢慢減速停止。它有一個(gè)削弱的效果,應(yīng)用的場景比如一扇門慢慢地關(guān)上,而不是砰地一聲。

kCAMediaTimingFunctionEaseInEaseOut創(chuàng)建了一個(gè)慢慢加速然后再慢慢減速的過程。這是現(xiàn)實(shí)世界大多數(shù)物體移動(dòng)的方式,也是大多數(shù)動(dòng)畫來說最好的選擇。如果只可以用一種緩沖函數(shù)的話,那就必須是它了。那么你會(huì)疑惑為什么這不是默認(rèn)的選擇,實(shí)際上當(dāng)使用UIView的動(dòng)畫方法時(shí),他的確是默認(rèn)的,但當(dāng)創(chuàng)建CAAnimation的時(shí)候,就需要手動(dòng)設(shè)置它了。

最后還有一個(gè)kCAMediaTimingFunctionDefault,它和kCAMediaTimingFunctionEaseInEaseOut很類似,但是加速和減速的過程都稍微有些慢。它和kCAMediaTimingFunctionEaseInEaseOut的區(qū)別很難察覺,可能是蘋果覺得它對(duì)于隱式動(dòng)畫來說更適合(然后對(duì)UIKit就改變了想法,而是使用kCAMediaTimingFunctionEaseInEaseOut作為默認(rèn)效果),雖然它的名字說是默認(rèn)的,但還是要記住當(dāng)創(chuàng)建顯式CAAnimation它并不是默認(rèn)選項(xiàng)(換句話說,默認(rèn)的圖層行為動(dòng)畫用kCAMediaTimingFunctionDefault作為它們的計(jì)時(shí)方法)。

你可以使用一個(gè)簡單的測試工程來實(shí)驗(yàn)一下(清單10.1),在運(yùn)行之前改變緩沖函數(shù)的代碼,然后點(diǎn)擊任何地方來觀察圖層是如何通過指定的緩沖移動(dòng)的。

清單10.1 緩沖函數(shù)的簡單測試

@interface ViewController ()

@property (nonatomic, strong) CALayer *colorLayer;

@end

@implementation ViewController

- (void)viewDidLoad
{
    [super viewDidLoad];
    //create a red layer
    self.colorLayer = [CALayer layer];
    self.colorLayer.frame = CGRectMake(0, 0, 100, 100);
    self.colorLayer.position = CGPointMake(self.view.bounds.size.width/2.0, self.view.bounds.size.height/2.0);
    self.colorLayer.backgroundColor = [UIColor redColor].CGColor;
    [self.view.layer addSublayer:self.colorLayer];
}

- (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event
{
    //configure the transaction
    [CATransaction begin];
    [CATransaction setAnimationDuration:1.0];
    [CATransaction setAnimationTimingFunction:[CAMediaTimingFunction functionWithName:kCAMediaTimingFunctionEaseOut]];
    //set the position
    self.colorLayer.position = [[touches anyObject] locationInView:self.view];
    //commit transaction
    [CATransaction commit];
}

@end

UIView的動(dòng)畫緩沖

UIKit的動(dòng)畫也同樣支持這些緩沖方法的使用,盡管語法和常量有些不同,為了改變UIView動(dòng)畫的緩沖選項(xiàng),給options參數(shù)添加如下常量之一:

UIViewAnimationOptionCurveEaseInOut 
UIViewAnimationOptionCurveEaseIn 
UIViewAnimationOptionCurveEaseOut 
UIViewAnimationOptionCurveLinear

它們和CAMediaTimingFunction緊密關(guān)聯(lián),UIViewAnimationOptionCurveEaseInOut是默認(rèn)值(這里沒有kCAMediaTimingFunctionDefault相對(duì)應(yīng)的值了)。

具體使用方法見清單10.2(注意到這里不再使用UIView額外添加的圖層,因?yàn)閁IKit的動(dòng)畫并不支持這類圖層)。

清單10.2 使用UIKit動(dòng)畫的緩沖測試工程

@interface ViewController ()

@property (nonatomic, strong) UIView *colorView;

@end

@implementation ViewController

- (void)viewDidLoad
{
    [super viewDidLoad];
    //create a red layer
    self.colorView = [[UIView alloc] init];
    self.colorView.bounds = CGRectMake(0, 0, 100, 100);
    self.colorView.center = CGPointMake(self.view.bounds.size.width / 2, self.view.bounds.size.height / 2);
    self.colorView.backgroundColor = [UIColor redColor];
    [self.view addSubview:self.colorView];
}

- (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event
{
    //perform the animation
    [UIView animateWithDuration:1.0 delay:0.0
                        options:UIViewAnimationOptionCurveEaseOut
                     animations:^{
                            //set the position
                            self.colorView.center = [[touches anyObject] locationInView:self.view];
                        }
                     completion:NULL];

}

@end

緩沖和關(guān)鍵幀動(dòng)畫

或許你會(huì)回想起第八章里面顏色切換的關(guān)鍵幀動(dòng)畫由于線性變換的原因(見清單8.5)看起來有些奇怪,使得顏色變換非常不自然。為了糾正這點(diǎn),我們來用更加合適的緩沖方法,例如kCAMediaTimingFunctionEaseIn,給圖層的顏色變化添加一點(diǎn)脈沖效果,讓它更像現(xiàn)實(shí)中的一個(gè)彩色燈泡。

我們不想給整個(gè)動(dòng)畫過程應(yīng)用這個(gè)效果,我們希望對(duì)每個(gè)動(dòng)畫的過程重復(fù)這樣的緩沖,于是每次顏色的變換都會(huì)有脈沖效果。

CAKeyframeAnimation有一個(gè)NSArray類型的timingFunctions屬性,我們可以用它來對(duì)每次動(dòng)畫的步驟指定不同的計(jì)時(shí)函數(shù)。但是指定函數(shù)的個(gè)數(shù)一定要等于keyframes數(shù)組的元素個(gè)數(shù)減一,因?yàn)樗敲枋雒恳粠g動(dòng)畫速度的函數(shù)。

在這個(gè)例子中,我們自始至終想使用同一個(gè)緩沖函數(shù),但我們同樣需要一個(gè)函數(shù)的數(shù)組來告訴動(dòng)畫不停地重復(fù)每個(gè)步驟,而不是在整個(gè)動(dòng)畫序列只做一次緩沖,我們簡單地使用包含多個(gè)相同函數(shù)拷貝的數(shù)組就可以了(見清單10.3)。

運(yùn)行更新后的代碼,你會(huì)發(fā)現(xiàn)動(dòng)畫看起來更加自然了。

清單10.3 對(duì)CAKeyframeAnimation使用CAMediaTimingFunction

@interface ViewController ()

@property (nonatomic, weak) IBOutlet UIView *layerView;
@property (nonatomic, weak) IBOutlet CALayer *colorLayer;

@end

@implementation ViewController

- (void)viewDidLoad
{
    [super viewDidLoad];
    //create sublayer
    self.colorLayer = [CALayer layer];
    self.colorLayer.frame = CGRectMake(50.0f, 50.0f, 100.0f, 100.0f);
    self.colorLayer.backgroundColor = [UIColor blueColor].CGColor;
    //add it to our view
    [self.layerView.layer addSublayer:self.colorLayer];
}

- (IBAction)changeColor
{
    //create a keyframe animation
    CAKeyframeAnimation *animation = [CAKeyframeAnimation animation];
    animation.keyPath = @"backgroundColor";
    animation.duration = 2.0;
    animation.values = @[
                         (__bridge id)[UIColor blueColor].CGColor,
                         (__bridge id)[UIColor redColor].CGColor,
                         (__bridge id)[UIColor greenColor].CGColor,
                         (__bridge id)[UIColor blueColor].CGColor ];
    //add timing function
    CAMediaTimingFunction *fn = [CAMediaTimingFunction functionWithName: kCAMediaTimingFunctionEaseIn];
    animation.timingFunctions = @[fn, fn, fn];
    //apply animation to layer
    [self.colorLayer addAnimation:animation forKey:nil];
}

@end
以上內(nèi)容是否對(duì)您有幫助:
在線筆記
App下載
App下載

掃描二維碼

下載編程獅App

公眾號(hào)
微信公眾號(hào)

編程獅公眾號(hào)