OC版 https://github.com/IFTTT/JazzHands
switf版 https://github.com/IFTTT/RazzleDazzle
簡介
Jazz Hand是一個(gè)簡單的基于keyframe的UIKit動(dòng)畫框架闲先。動(dòng)畫可以通過手勢(shì),scrollviews,Kvo或者ReactiveCocoa控制。常見用途:制作一個(gè)拖動(dòng)動(dòng)畫的引導(dǎo)頁.以下是我寫的一個(gè)Demo.

安裝
OC版:
pod "JazzHands"
swift版:
pod "RazzleDazzle"
類圖

IFTTTAnimatior
導(dǎo)演類,里面有一個(gè)動(dòng)畫(IFTTTAnimation
)數(shù)組管理所有動(dòng)畫的添加/刪除/執(zhí)行.IFTTTAnimatable
執(zhí)行動(dòng)畫類.
重要: 定義了核心協(xié)議(接口),animate:(CGFloat)time;
計(jì)算每一個(gè)時(shí)間點(diǎn)當(dāng)前對(duì)象的值.每一種動(dòng)畫都要實(shí)現(xiàn)這個(gè)接口.如IFTTTAlphaAnimation
類,計(jì)算一個(gè)時(shí)間點(diǎn)對(duì)應(yīng)的對(duì)象的alpha值-
IFTTTAnimation
動(dòng)畫基類,定義了一個(gè)對(duì)象的動(dòng)畫.僅僅只是對(duì)類中的IFTTTFilemstrip
進(jìn)行一個(gè)簡單的封裝.注: 每種動(dòng)畫都繼承IFTTTAnimation和實(shí)現(xiàn)<IFTTTAnimatable>協(xié)議才會(huì)正常工作.
IFTTTFilmstrip
膠片類,有一個(gè)關(guān)鍵幀(IFTTTKeyframe)數(shù)組.,添加/修改/獲取對(duì)應(yīng)關(guān)鍵幀(IFTTTKeyframe)的值.通過valueAtTime:可以計(jì)算兩個(gè)相鄰關(guān)鍵幀之間的的值.這個(gè)值在執(zhí)行動(dòng)畫時(shí)- animate
使用.IFTTTKeyframe
關(guān)鍵幀類,描述每一個(gè)關(guān)鍵幀的時(shí)間對(duì)應(yīng)的值.
分析:
jazzhand主要應(yīng)用在scrollviews
,同時(shí)封裝了IFTTTAnimatedPagingScrollViewController
方便我們繼承使用.目前這個(gè)類scrollview
只支持橫向滾動(dòng),并不支持縱向滾動(dòng).jazzhand框架是基于關(guān)鍵幀的動(dòng)畫,這個(gè)概念其實(shí)跟Core Animation
的概念是一樣的. 只不過跟core Animation有不同一點(diǎn)的是,jazzhand框架的動(dòng)畫驅(qū)動(dòng)是坐標(biāo)驅(qū)動(dòng).
舉個(gè)例子,在Core Animation
中,我們只需要設(shè)置軌跡,方向,時(shí)間就可以提交了.接下就由Core Animation
負(fù)責(zé)計(jì)算,就可以看到App能執(zhí)行一段動(dòng)畫.這里設(shè)置了時(shí)間5s.在APP接下來的5s中,Core Animation
每1/30秒重新計(jì)算一次imageview
的位置并調(diào)用[self ifNeedLayout]
方法進(jìn)行更行.然后就形成了我們所看到的動(dòng)畫.
[UIView beginAnimations:@"jk" context:nil];
// 設(shè)置動(dòng)畫的方向
[UIView setAnimationTransition:UIViewAnimationTransitionFlipFromRight forView:imageView cache:YES];
// 設(shè)置動(dòng)畫持續(xù)時(shí)間
[UIView setAnimationDuration:5];
// 設(shè)置動(dòng)畫效果過渡的狀態(tài)
[UIView setAnimationCurve:UIViewAnimationCurveEaseIn];
// 提交動(dòng)畫
[UIView commitAnimations];
}
但是Jazzhand中,卻以scrollview.contentOffset
作為動(dòng)畫執(zhí)行的time,在[scrollview didScroll]
代理中重新計(jì)算視圖的位置.

在Jazzhand中有一個(gè)重要的方法
- (void)keepView:(UIView *)view
onPages:(NSArray *)pages
atTimes:(NSArray *)times
withOffsets:(NSArray *)offsets
withAttribute:(IFTTTHorizontalPositionAttribute)attribute
pages和offsets的個(gè)數(shù)要相同.不然會(huì)產(chǎn)生致命錯(cuò)誤(crash)
其中每個(gè)page和time對(duì)應(yīng)的是:
view.frame.size.x = page * pageWidth
scrollview.contentOffset = time * pageWidth
當(dāng)time = 1時(shí), 即scrollview滾到第二頁的時(shí)候,scrollview.contentOffset=(pageWidth * 1).然后我們必須設(shè)置時(shí)間time所對(duì)應(yīng)的view的位置.可以想象每拖動(dòng)一個(gè)頁面,相當(dāng)于view對(duì)象進(jìn)行時(shí)間0到1的動(dòng)畫.
每一個(gè)動(dòng)畫(animation)都擁有一個(gè)膠卷(filmstrip),每一個(gè)膠卷都包含該了該動(dòng)作的所有關(guān)鍵幀(keyframe).隨著屏幕拖拽,JazzHands會(huì)根據(jù)約束和時(shí)間(contentOffset)計(jì)算對(duì)象的位置.只要刷新的頻率只夠高.我們?nèi)搜劬涂床怀鍪侵匦庐嬌先?而是連續(xù)的動(dòng)畫了.
使用:
- @interface IntrotductionController:IFTTTAnimatedPagingScrollViewController
IFTTTAnimatedPagingScrollViewController
繼承viewcontroller
,有子成員scrollview
,封裝了contentview
.所以只需要將對(duì)象加入到contentview
中就可以.
- 重載
numberOfPage
方法系統(tǒng)默認(rèn)是2 - 如果不需要,就不要給對(duì)象添加約束.
不要給x值添加約束. 原因見4.
-
[self keepView:self.circle onPages:@[@(0), @(1)]];
設(shè)置對(duì)象的x值的關(guān)鍵幀. 這句代碼的意思是,當(dāng)視圖view移動(dòng)到scrollview
的contentOffset
為0時(shí)circle
的位置也為0, 當(dāng)拖動(dòng)到contentOffset
為1時(shí),circle
的位置也是1,這句代碼內(nèi)部幫對(duì)象添加了x值的約束.如果你之前為x值添加其他約束,這里在運(yùn)行時(shí)候回出現(xiàn)約束沖突錯(cuò)誤.同時(shí)我們只能選擇對(duì)象在屏幕中左中右三個(gè)相對(duì)位置.
注: 使對(duì)象相對(duì)于屏幕位置不動(dòng)的方法
// 設(shè)置time即contentOffset不變,即使相對(duì)屏幕不變.
// 在index-1滑動(dòng)到index時(shí),leimuImgHeart的位置從index-1.15滑動(dòng)到index-0.15.兩者滑動(dòng)的距離相等,速度相等.
// 所以視覺上是相對(duì)于屏幕位置不變.同時(shí),第4中不設(shè)置times參數(shù)也是默認(rèn)相對(duì)于屏幕位置不變
[self keepView:leimuImgHeart onPages:@[@(index-1.15),@(index-0.15), @(index-1.15)] atTimes:@[@(index-1),@(index),@(index+1)] withAttribute:IFTTTHorizontalPositionAttributeCenterX offset:0];
Time變化引起offset變化,leimuImgHeart也得同步跟time一起變化
-
定義你自己的動(dòng)畫
// 最好使用autolayout固定視圖的位置 NSLayoutConstraint * topConstraint = [NSLayoutConstraint constraintWithItem:leimuImgHeart attribute:NSLayoutAttributeCenterY relatedBy:NSLayoutRelationEqual toItem:self.contentView attribute:NSLayoutAttributeTop multiplier:1.0 constant:0.f]; [self.contentView addConstraint:topConstraint]; // 添加一個(gè)約束幀動(dòng)畫,即約束隨time的變化而變化 IFTTTConstraintMultiplierAnimation *constantAnimation = [IFTTTConstraintMultiplierAnimation animationWithSuperview:self.contentView constraint:topConstraint attribute:IFTTTLayoutAttributeHeight referenceView:self.contentView]; [constantAnimation addKeyframeForTime:index-1 multiplier:-0.2f]; [constantAnimation addKeyframeForTime:index multiplier:0.3f]; [self.animator addAnimation:constantAnimation];