<code>JHChainableAnimations</code>代碼閱讀
<strong><code>JHChainableAnimations</code>2.0版本代碼還是有點(diǎn)變化的,本篇章閱讀的是2.0之前的版本,原理上基本一致(別問我為什么,因?yàn)轫?xiàng)目中直接拉的源碼是2.0之前的),以后有空分析一下2.0的變化部分</strong>
讀懂代碼之前,最好能提前看一下 <code>CAKeyframeAnimation</code>,因?yàn)閯?dòng)畫最后的操作,最終都是通過這個(gè)完成的。
<code> JHKeyframeAnimation </code>聲明如下:
@interface JHKeyframeAnimation : CAKeyframeAnimation
// From https://github.com/NachoSoto/NSBKeyframeAnimation
typedef double(^NSBKeyframeAnimationFunctionBlock)(double t, double b, double c, double d);
@property (nonatomic, copy) NSBKeyframeAnimationFunctionBlock functionBlock;
@property(strong, nonatomic) id fromValue;
@property(strong, nonatomic) id toValue;
-(void) calculate;
@end
本文假設(shè)已經(jīng)了解過<code>CAKeyframeAnimation</code>,所以對(duì)此不做深入討論
<code>JHChainableAnimations</code> 是一個(gè)方便進(jìn)行動(dòng)畫操作的三方庫(kù),是基于<code>CAAnimation</code>進(jìn)行封裝的
<code>JHChainableAnimations</code>支持鏈?zhǔn)秸{(diào)用。
鏈?zhǔn)秸{(diào)用的實(shí)現(xiàn)如下:
-(JHChainableFloat)moveY {
JHChainableFloat chainable = JHChainableFloat(f) {
······
······
return self;
};
return chainable;
}
函數(shù)返回一個(gè)<code>block block</code>中返回<code>self</code>所以在執(zhí)行完bock之后可以繼續(xù)調(diào)用self的方法。
下面通過一個(gè)栗子來看看具體是如何實(shí)現(xiàn)的。
upperView.bounce.moveY(100).animateWithCompletion(0.2f, JHAnimationCompletion(){
///TODO THINGS
});
栗子的作用是<code>Y</code>方向移動(dòng)100 0.2s完成,并且指定了完成這個(gè)動(dòng)畫后做的block
要讀懂代碼,首先要知道幾個(gè)數(shù)組,所有動(dòng)畫開始,執(zhí)行,結(jié)束的block都放在這些數(shù)組中
// Arrays of animations to be grouped
@property (strong, nonatomic) NSMutableArray *JHAnimations;
// Grouped animations
@property (strong, nonatomic) NSMutableArray *JHAnimationGroups;
// Code run at the beginning of an animation link to calculate values
@property (strong, nonatomic) NSMutableArray *JHAnimationCalculationActions;
// Code run after animation is completed
@property (strong, nonatomic) NSMutableArray *JHAnimationCompletionActions;
就不做翻譯了,就是動(dòng)畫執(zhí)行前,執(zhí)行的動(dòng)畫,執(zhí)行動(dòng)畫的group 以及動(dòng)畫執(zhí)行后的block數(shù)組.
-(JHChainableFloat)moveY {
JHChainableFloat chainable = JHChainableFloat(f) {
/// 把block加入到動(dòng)畫執(zhí)行前的數(shù)組中
[self addAnimationCalculationAction:^(UIView *weakSelf) {
JHKeyframeAnimation *positionAnimation = [weakSelf basicAnimationForKeyPath:@"position.y"];
positionAnimation.fromValue = @(weakSelf.layer.position.y);
positionAnimation.toValue = @(weakSelf.layer.position.y+f);
/// 要在CAAnimationGroup 中執(zhí)行的動(dòng)畫,統(tǒng)一放在JHAnimations 數(shù)組中,
[weakSelf addAnimationFromCalculationBlock:positionAnimation];
}];
/// 把 block 加入到動(dòng)畫執(zhí)行完成后的數(shù)組中
[self addAnimationCompletionAction:^(UIView *weakSelf) {
CGPoint position = weakSelf.layer.position;
position.y += f;
weakSelf.layer.position = position;
}];
return self;
};
return chainable;
}
<code> moveY </code>做的事情比較簡(jiǎn)單
- 生成動(dòng)畫開始執(zhí)行前的block 加入到開始執(zhí)行前的的鏈?zhǔn)秸{(diào)用數(shù)組中,該block的主要作用是 生成 <code> JHKeyframeAnimation </code>對(duì)象并且放入到數(shù)組中,這個(gè)數(shù)組的動(dòng)畫是要放入<code> CAAnimationGroup </code>中的。<strong>A</strong>,我們標(biāo)記一下,后面會(huì)用到這個(gè)block
- 生成動(dòng)畫完成后的block ,并且加入完成后的數(shù)組<code>JHAnimationCompletionActions</code>中。
-(void) addAnimationCalculationAction:(JHAnimationCalculationAction)action {
NSMutableArray *actions = [self.JHAnimationCalculationActions lastObject];
[actions addObject:action];
}
<code> addAnimationCalculationAction </code>加單的把block加入到 <code>JHAnimationCalculationActions</code> 數(shù)組中,取出last 數(shù)組,每一組動(dòng)畫都是單獨(dú)放在一個(gè)數(shù)組中,鏈?zhǔn)秸{(diào)用有可能有好幾組動(dòng)畫過程。每一個(gè)動(dòng)畫過程放在一個(gè)單獨(dú)的數(shù)組。其他的數(shù)組作用也是這樣子。
-(JHChainableAnimationWithCompletion)animateWithCompletion {
JHChainableAnimationWithCompletion chainable = JHChainableAnimationWithCompletion(duration, completion) {
/// 取出動(dòng)畫group
CAAnimationGroup *group = [self.JHAnimationGroups lastObject];
/// 設(shè)置時(shí)間
group.duration = duration;
/// 設(shè)置動(dòng)畫完成后的block
self.animationCompletion = completion;
/// 開始動(dòng)畫過程
[self animateChain];
/// 返回self 支持鏈?zhǔn)秸{(diào)用
return self;
};
return chainable;
}
<code> animateWithCompletion </code> 中設(shè)置了<code>group</code>, 然后調(diào)用 <code> animateChain </code>執(zhí)行動(dòng)畫。
-(void) animateChain {
/// 檢查數(shù)據(jù)是否合法
[self sanityCheck];
[CATransaction begin];
[CATransaction setDisableActions:YES];
/// 設(shè)置動(dòng)畫完成后的block
[CATransaction setCompletionBlock:^{
[self.layer removeAnimationForKey:@"AnimationChain"];
/// 會(huì)檢查動(dòng)畫數(shù)組里還有沒有要執(zhí)行的動(dòng)畫,如果沒有clean 如果有,繼續(xù)調(diào)用 animateChain 函數(shù)。鏈?zhǔn)秸{(diào)用的精髓這里
[self chainLinkDidFinishAnimating];
}];
/// 執(zhí)行動(dòng)畫過程
[self animateChainLink];
[CATransaction commit];
[self executeCompletionActions];
}
-(void) animateChainLink {
/// 錨點(diǎn)放在中心點(diǎn)
[self makeAnchorFromX:0.5 Y:0.5];
NSMutableArray *actionCluster = [self.JHAnimationCalculationActions firstObject];
/// 調(diào)用剛才 標(biāo)記的 A 處
for (JHAnimationCalculationAction action in actionCluster) {
__weak UIView *weakSelf = self;
/// 執(zhí)行 動(dòng)畫開始前的block 生成 JHKeyframeAnimation 對(duì)象,并且加入到動(dòng)畫的數(shù)組中
action(weakSelf);
}
CAAnimationGroup *group = [self.JHAnimationGroups firstObject];
NSMutableArray *animationCluster = [self.JHAnimations firstObject];
for (JHKeyframeAnimation *animation in animationCluster) {
animation.duration = group.duration;
/// 生成 values 數(shù)組,(動(dòng)畫過程)
[animation calculate];
}
group.animations = animationCluster;
[self.layer addAnimation:group forKey:@"AnimationChain"];
// 更新一下約束。
NSTimeInterval delay = MAX(group.beginTime - CACurrentMediaTime(), 0.0);
[self.class animateWithDuration:group.duration
delay:delay
options:0
animations:^{
[self updateConstraints];
} completion:nil];
}
animateChainLink 函數(shù)首先把錨點(diǎn)放在中心,然后執(zhí)行 剛才標(biāo)記的 A 處的block(生成JHKeyframeAnimation 對(duì)象,并且放入 動(dòng)畫數(shù)組中)
-(void) chainLinkDidFinishAnimating {
/// 刪除已經(jīng)執(zhí)行過的動(dòng)畫
[self.JHAnimationCompletionActions removeObjectAtIndex:0];
[self.JHAnimationCalculationActions removeObjectAtIndex:0];
[self.JHAnimations removeObjectAtIndex:0];
/// 刪除已經(jīng)執(zhí)行過的group
[self.JHAnimationGroups removeObjectAtIndex:0];
[self sanityCheck];
/// 如果響應(yīng)連中沒有要執(zhí)行的動(dòng)畫了就清空
if (self.JHAnimationGroups.count == 0) {
[self clear];
if (self.animationCompletion) {
JHAnimationCompletion completion = self.animationCompletion;
self.animationCompletion = nil;
completion();
}
}
else {
/// 如果響應(yīng)鏈中還有要執(zhí)行的動(dòng)畫,繼續(xù)調(diào)用
[self animateChain];
}
}
<code>chainLinkDidFinishAnimating</code>一組動(dòng)畫執(zhí)行完畢后,結(jié)束的調(diào)用。
- 首先會(huì)刪除已經(jīng)執(zhí)行過的動(dòng)畫。
- 然后檢查一下剩余動(dòng)畫group的的數(shù)量是否合法。
- 如果有剩余的動(dòng)畫組沒執(zhí)行,則繼續(xù)調(diào)用<code>animateChain</code>,如果沒有,則調(diào)用<code>animationCompletion</code>block
<strong>下面代碼過程就是根據(jù) JHKeyframeAnimation 的 fromValue 和 toValue 計(jì)算出一組值,賦值給 values對(duì)象。 </strong>
-(void) calculate {
[self createValueArray];
}
- (void) createValueArray {
if (self.fromValue && self.toValue && self.duration) {
if ([self.fromValue isKindOfClass:[NSNumber class]] && [self.toValue isKindOfClass:[NSNumber class]]) {
self.values = [self valueArrayForStartValue:[self.fromValue floatValue] endValue:[self.toValue floatValue]];
else {
···
···
}
- (NSArray*) valueArrayForStartValue:(CGFloat)startValue endValue:(CGFloat)endValue {
NSUInteger steps = (NSUInteger)ceil(kFPS * self.duration) + 2;
NSMutableArray *valueArray = [NSMutableArray arrayWithCapacity:steps];
const double increment = 1.0 / (double)(steps - 1);
double progress = 0.0,
v = 0.0,
value = 0.0;
NSUInteger i;
for (i = 0; i < steps; i++)
{
v = self.functionBlock(self.duration * progress * 1000, 0, 1, self.duration * 1000);
value = startValue + v * (endValue - startValue);
[valueArray addObject:@(value)];
progress += increment;
}
return [NSArray arrayWithArray:valueArray];
}
<code> valueArrayForStartValue </code> 計(jì)算出一組值賦值給<code>values</code>