POP簡單介紹與使用實踐

weixin_33924312發表於2017-12-27

前言

動畫在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 :)

使用

引入標頭檔案

#import <pop/POP.h>
複製程式碼

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立馬就活潑起來了 有木有?!)

示例程式碼2

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:@"%02d:%02d:%02d",(int)values[0]/60,(int)values[0]%60,(int)(values[0]*100)%100];
        };

//        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能做的事情還不少 :)

相關文章