作者:裡脊串 授權本站轉載。
前言
動畫在APP開發過程中,大家多多少少都會接觸到,而且隨著iOS7的扁平化風格啟用之後,越來越多的APP開始嘗試加入各種絢麗的動畫交互效果以增加APP的用戶體驗。(當然,還是以國外的APP居多)
有過相關開發經驗的同學肯定知道在iOS中,動畫相關的部分都是基於Core Animation,但是今天我們不討論Core Animation。今天的主角是POP -來自於Facebook的動畫引擎(其實我不喜歡把POP定義為動畫引擎 我願意稱它為函數發生器)
介紹
官方地址 https://github.com/facebook/pop
官方介紹(翻譯版)
POP是一個在iOS與OS X上通用的極具擴展性的動畫引擎。它在基本的靜態動畫的基礎上增加的彈簧動畫與衰減動畫,使之能創造出更真實更具物理性的交互動畫。POP的API可以快速的與現有的ObjC代碼集成並可以作用於任意對象的任意屬性。
POP是個相當成熟且久經考驗的框架,Facebook出品的令人驚歎的Paper應用中的所有動畫和效果即出自POP。
安裝方式還是推薦使用CocoaPod。
pod 'pop', '~> 1.0'
POP的神奇之處在於,它是獨立與Core Animation的存在。所以,忘記Core Animation吧,忘記Layer Tree吧,迎接一個簡單的明天!(LOL 開玩笑的~:) 很多地方還是會需要Core Animation的,不過說不定哪天蘋果大發善心,將動畫相關的部分向POP借鑒一點也不是不可能的(比如SpriteKit就借鑒了Cocos2D :)
使用
POP默認支持三種動畫,但同時也支持自定義動畫。
POPBasicAnimation
POPSpringAnimation
POPDecayAnimation
POPCustomAnimation //自定義動畫
這裡我們只討論前三種(因為自定義動畫我也沒用過 :) 先來看看官方的示例代碼吧。
官方代碼示例
//Basic animations can be used to interpolate values over a specified time period. To use an ease-in ease-out animation to animate a view's alpha from 0.0 to 1.0 over the default duration: POPBasicAnimation *anim = [POPBasicAnimation animationWithPropertyNamed:kPOPViewAlpha]; anim.fromValue = @(0.0); anim.toValue = @(1.0); [view pop_addAnimation:anim forKey:@"fade"]; //Spring animations can be used to give objects a delightful bounce. In this example, we use a spring animation to animate a layer's bounds from its current value to (0, 0, 400, 400): POPSpringAnimation *anim = [POPSpringAnimation animationWithPropertyNamed:kPOPLayerBounds]; anim.toValue = [NSValue valueWithCGRect:CGRectMake(0, 0, 400, 400)]; [layer pop_addAnimation:anim forKey:@"size"]; //Decay animations can be used to gradually slow an object to a halt. In this example, we decay a layer's positionX from it's current value and velocity 1000pts per second: POPDecayAnimation *anim = [POPDecayAnimation animationWithPropertyNamed:kPOPLayerPositionX]; anim.velocity = @(1000.); [layer pop_addAnimation:anim forKey:@"slide"];
POPBasicAnimation
POPBasicAnimation使用最廣泛 提供固定時間間隔的動畫(如淡入淡出效果)
代碼示例1
POPBasicAnimation *anBasic = [POPBasicAnimation animationWithPropertyNamed:kPOPLayerPositionX]; anBasic.toValue = @(self.square.center.y+300); anBasic.beginTime = CACurrentMediaTime() + 1.0f; [self.square pop_addAnimation:anBasic forKey:@"position"];
其動畫效果如下
可以看到,添加一個動畫最少僅需三步
定義一個animation對象,並指定對應的動畫屬性
設置初始值和默認值(初始值可以不指定,會默認從當前值開始)
添加到想產生動畫的對象上
POPBasicAnimation可配置的屬性與默認值為
duration:0.4 //動畫間隔
POPBasicAnimation提供四種timingfunction(很熟悉,對不對? 就是Core Animation中那些)
kCAMediaTimingFunctionLinear
kCAMediaTimingFunctionEaseIn
kCAMediaTimingFunctionEaseOut
kCAMediaTimingFunctionEaseInEaseOut
其時間函數分別如下
POPSpringAnimation
POPSpringAnimation也許是大多數人使用POP的理由,其提供一個類似彈簧一般的動畫效果(使用後,APP立馬就活潑起來了,有木有?!)
代碼示例23
POPSpringAnimation *anSpring = [POPSpringAnimation animationWithPropertyNamed:kPOPLayerPositionX]; anSpring.toValue = @(self.square.center.y+300); anSpring.beginTime = CACurrentMediaTime() + 1.0f; anSpring.springBounciness = 10.0f; [self.square pop_addAnimation:anSpring forKey:@"position"];
其動畫效果如下
POPSpringAnimation可配置的屬性與默認值為
springBounciness:4.0 //[0-20] 彈力 越大則震動幅度越大 springSpeed :12.0 //[0-20] 速度 越大則動畫結束越快 dynamicsTension :0 //拉力 接下來這三個都跟物理力學模擬相關 數值調整起來也很費時 沒事不建議使用哈 dynamicsFriction:0 //摩擦 同上 dynamicsMass :0 //質量 同上
注意:POPSpringAnimation是沒有duration字段的,其動畫持續時間由以上幾個參數決定
其時間函數如下
POPDecayAnimation
POPDecayAnimation提供一個過阻尼效果(其實Spring是一種欠阻尼效果),可以實現類似UIScrollView的滑動衰減效果(是的 你可以靠它來自己實現一個UIScrollView)
代碼示例3
POPDecayAnimation *anDecay = [POPDecayAnimation animationWithPropertyNamed:kPOPLayerPositionX]; anDecay.velocity = @(600); anDecay.beginTime = CACurrentMediaTime() + 1.0f; [self.square pop_addAnimation:anDecay forKey:@"position"];
其動畫效果如下
注意:這裡對POPDecayAnimation設置toValue是沒有意義的,會被忽略(因為目的狀態是動態計算得到的)
POPDecayAnimation可配置的屬性與默認值為
deceleration:0.998 //衰減系數(越小則衰減得越快)
注意:POPDecayAnimation也是沒有duration字段的,其動畫持續時間由velocity與deceleration決定
其時間函數如下
接下來我們看一下POP默認支持哪些屬性的動畫。打開POPAnimatablePropery.h可以看到如下定義(這些是到目前為止 所支持的屬性 隨著版本的更新 還在不斷的新增中 :)
/** Common CALayer property names. */ extern NSString * const kPOPLayerBackgroundColor; extern NSString * const kPOPLayerBounds; extern NSString * const kPOPLayerCornerRadius; extern NSString * const kPOPLayerBorderWidth; extern NSString * const kPOPLayerBorderColor; extern NSString * const kPOPLayerOpacity; extern NSString * const kPOPLayerPosition; extern NSString * const kPOPLayerPositionX; extern NSString * const kPOPLayerPositionY; extern NSString * const kPOPLayerRotation; extern NSString * const kPOPLayerRotationX; extern NSString * const kPOPLayerRotationY; extern NSString * const kPOPLayerScaleX; extern NSString * const kPOPLayerScaleXY; extern NSString * const kPOPLayerScaleY; extern NSString * const kPOPLayerSize; extern NSString * const kPOPLayerSubscaleXY; extern NSString * const kPOPLayerSubtranslationX; extern NSString * const kPOPLayerSubtranslationXY; extern NSString * const kPOPLayerSubtranslationY; extern NSString * const kPOPLayerSubtranslationZ; extern NSString * const kPOPLayerTranslationX; extern NSString * const kPOPLayerTranslationXY; extern NSString * const kPOPLayerTranslationY; extern NSString * const kPOPLayerTranslationZ; extern NSString * const kPOPLayerZPosition; extern NSString * const kPOPLayerShadowColor; extern NSString * const kPOPLayerShadowOffset; extern NSString * const kPOPLayerShadowOpacity; extern NSString * const kPOPLayerShadowRadius; /** Common CAShapeLayer property names. */ extern NSString * const kPOPShapeLayerStrokeStart; extern NSString * const kPOPShapeLayerStrokeEnd; extern NSString * const kPOPShapeLayerStrokeColor; extern NSString * const kPOPShapeLayerFillColor; /** Common NSLayoutConstraint property names. */ extern NSString * const kPOPLayoutConstraintConstant; #if TARGET_OS_IPHONE /** Common UIView property names. */ extern NSString * const kPOPViewAlpha; extern NSString * const kPOPViewBackgroundColor; extern NSString * const kPOPViewBounds; extern NSString * const kPOPViewCenter; extern NSString * const kPOPViewFrame; extern NSString * const kPOPViewScaleX; extern NSString * const kPOPViewScaleXY; extern NSString * const kPOPViewScaleY; extern NSString * const kPOPViewSize; extern NSString * const kPOPViewTintColor; /** Common UIScrollView property names. */ extern NSString * const kPOPScrollViewContentOffset; extern NSString * const kPOPScrollViewContentSize; extern NSString * const kPOPScrollViewZoomScale; extern NSString * const kPOPScrollViewContentInset; /** Common UITableView property names. */ extern NSString * const kPOPTableViewContentOffset; extern NSString * const kPOPTableViewContentSize; /** Common UICollectionView property names. */ extern NSString * const kPOPCollectionViewContentOffset; extern NSString * const kPOPCollectionViewContentSize; /** Common UINavigationBar property names. */ extern NSString * const kPOPNavigationBarBarTintColor; /** Common UIToolbar property names. */ extern NSString * const kPOPToolbarBarTintColor; /** Common UITabBar property names. */ extern NSString * const kPOPTabBarBarTintColor; /** Common UILabel property names. */ extern NSString * const kPOPLabelTextColor;
作為剛接觸POP的一些同學來說,如果在上面看到你希望的某些屬性的話,你可以像官方代碼示例一樣指定這個屬性即可開始動畫了
但是如果你想要的某些屬性不在之上呢,這時候自定義屬性POPAnimatableProperty就排上用場了
自定義屬性
POP默認支持的三種動畫都繼承自POPPropertyAnimation。POPPropertyAnimation中定義了一個叫property的屬性( 之前沒有用到它是因為POP根據不同的默認動畫屬性幫你生成了默認的property) 而這個property則是用來驅動POP的動畫效果中的重要一環。
代碼示例4
POPAnimatableProperty *prop = [POPAnimatableProperty propertyWithName:@"prop" initializer:^(POPMutableAnimatableProperty *prop) { // read value prop.readBlock = ^(id obj, CGFloat values[]) { }; // write value prop.writeBlock = ^(id obj, const CGFloat values[]) { }; // dynamics threshold prop.threshold = 0.01; }];
其組成就是一個readBlock一個writeBlock和一個threashold
readBlock告訴POP當前的屬性值
writeBlock中修改變化後的屬性值
threashold決定了動畫變化間隔的阈值,值越大writeBlock的調用次數越少
POPAnimatableProperty其實是POP中一個比較重要的東西,像上面提到的POP自帶的動畫屬性,查看源代碼可以看到也只是POP自動幫你設置好了POPAnimatableProperty而已,其作用就是當動畫的某個時間片被觸發時,告訴系統如何根據當前時間片做出變化。
還是以一個實際的例子來說明如何使用自定義屬性,比如我們要實現一個像系統的時鐘APP裡秒表計時的一個效果。
POPAnimatableProperty *prop = [POPAnimatableProperty propertyWithName:@"countdown" initializer:^(POPMutableAnimatableProperty *prop) { prop.writeBlock = ^(id obj, const CGFloat values[]) { UILabel *lable = (UILabel*)obj; label.text = [NSString stringWithFormat:@"d:d:d",(int)values[0]/60,(int)values[0]%60,(int)(values[0]*100)0]; }; // prop.threshold = 0.01f; }]; POPBasicAnimation *anBasic = [POPBasicAnimation linearAnimation]; //秒表當然必須是線性的時間函數 anBasic.property = prop; //自定義屬性 anBasic.fromValue = @(0); //從0開始 anBasic.toValue = @(3*60); //180秒 anBasic.duration = 3*60; //持續3分鐘 anBasic.beginTime = CACurrentMediaTime() + 1.0f; //延遲1秒開始 [label pop_addAnimation:anBasic forKey:@"countdown"];
其動畫效果如下
有沒有從中得到一些啟發呢? POP可以做的事情可遠比Core Animation要多(注意這裡我們使用了beginTime這個屬性來設置動畫的延遲施放) 例如音樂播放時那種淡入淡出的效果等等也可以用POP來實現
小結
其實只需要熟練掌握POP自帶的三種動畫,即可完成大部分的動畫效果。如果實在是無法滿足你的需求的話,自定義動畫也基本可以滿足你的要求。可以說POP化繁為簡的出現,極大的方便了我們這些苦逼的coder。
當然,就像我說的,POP不僅僅是一個動畫引擎。相信經過我最後一個例子,大家可以得到一點啟示,POP能做的事情還不少 :)