JHChainableAnimations
代码阅读
JHChainableAnimations
2.0版本代码还是有点变化的,本篇章阅读的是2.0之前的版本,原理上基本一致(别问我为什么,因为项目中直接拉的源码是2.0之前的),以后有空分析一下2.0的变化部分
读懂代码之前,最好能提前看一下 CAKeyframeAnimation
,因为动画最后的操作,最终都是通过这个完成的。
JHKeyframeAnimation
声明如下:
@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
本文假设已经了解过CAKeyframeAnimation
,所以对此不做深入讨论
JHChainableAnimations
是一个方便进行动画操作的三方库,是基于CAAnimation
进行封装的
JHChainableAnimations
支持链式调用。
链式调用的实现如下:
-(JHChainableFloat)moveY {
JHChainableFloat chainable = JHChainableFloat(f) {
······
······
return self;
};
return chainable;
}
函数返回一个
block block
中返回self
所以在执行完bock之后可以继续调用self的方法。
下面通过一个栗子来看看具体是如何实现的。
upperView.bounce.moveY(100).animateWithCompletion(0.2f, JHAnimationCompletion(){
///TODO THINGS
});
栗子的作用是
Y
方向移动100 0.2s完成,并且指定了完成这个动画后做的block
要读懂代码,首先要知道几个数组,所有动画开始,执行,结束的block都放在这些数组中
// 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;
就不做翻译了,就是动画执行前,执行的动画,执行动画的group 以及动画执行后的block数组.
-(JHChainableFloat)moveY {
JHChainableFloat chainable = JHChainableFloat(f) {
/// 把block加入到动画执行前的数组中
[self addAnimationCalculationAction:^(UIView *weakSelf) {
JHKeyframeAnimation *positionAnimation = [weakSelf basicAnimationForKeyPath:@"position.y"];
positionAnimation.fromValue = @(weakSelf.layer.position.y);
positionAnimation.toValue = @(weakSelf.layer.position.y+f);
/// 要在CAAnimationGroup 中执行的动画,统一放在JHAnimations 数组中,
[weakSelf addAnimationFromCalculationBlock:positionAnimation];
}];
/// 把 block 加入到动画执行完成后的数组中
[self addAnimationCompletionAction:^(UIView *weakSelf) {
CGPoint position = weakSelf.layer.position;
position.y += f;
weakSelf.layer.position = position;
}];
return self;
};
return chainable;
}
moveY
做的事情比较简单
- 生成动画开始执行前的block 加入到开始执行前的的链式调用数组中,该block的主要作用是 生成
JHKeyframeAnimation
对象并且放入到数组中,这个数组的动画是要放入CAAnimationGroup
中的。A,我们标记一下,后面会用到这个block - 生成动画完成后的block ,并且加入完成后的数组
JHAnimationCompletionActions
中。
-(void) addAnimationCalculationAction:(JHAnimationCalculationAction)action {
NSMutableArray *actions = [self.JHAnimationCalculationActions lastObject];
[actions addObject:action];
}
addAnimationCalculationAction
加单的把block加入到JHAnimationCalculationActions
数组中,取出last 数组,每一组动画都是单独放在一个数组中,链式调用有可能有好几组动画过程。每一个动画过程放在一个单独的数组。其他的数组作用也是这样子。
-(JHChainableAnimationWithCompletion)animateWithCompletion {
JHChainableAnimationWithCompletion chainable = JHChainableAnimationWithCompletion(duration, completion) {
/// 取出动画group
CAAnimationGroup *group = [self.JHAnimationGroups lastObject];
/// 设置时间
group.duration = duration;
/// 设置动画完成后的block
self.animationCompletion = completion;
/// 开始动画过程
[self animateChain];
/// 返回self 支持链式调用
return self;
};
return chainable;
}
animateWithCompletion
中设置了group
, 然后调用animateChain
执行动画。
-(void) animateChain {
/// 检查数据是否合法
[self sanityCheck];
[CATransaction begin];
[CATransaction setDisableActions:YES];
/// 设置动画完成后的block
[CATransaction setCompletionBlock:^{
[self.layer removeAnimationForKey:@"AnimationChain"];
/// 会检查动画数组里还有没有要执行的动画,如果没有clean 如果有,继续调用 animateChain 函数。链式调用的精髓这里
[self chainLinkDidFinishAnimating];
}];
/// 执行动画过程
[self animateChainLink];
[CATransaction commit];
[self executeCompletionActions];
}
-(void) animateChainLink {
/// 锚点放在中心点
[self makeAnchorFromX:0.5 Y:0.5];
NSMutableArray *actionCluster = [self.JHAnimationCalculationActions firstObject];
/// 调用刚才 标记的 A 处
for (JHAnimationCalculationAction action in actionCluster) {
__weak UIView *weakSelf = self;
/// 执行 动画开始前的block 生成 JHKeyframeAnimation 对象,并且加入到动画的数组中
action(weakSelf);
}
CAAnimationGroup *group = [self.JHAnimationGroups firstObject];
NSMutableArray *animationCluster = [self.JHAnimations firstObject];
for (JHKeyframeAnimation *animation in animationCluster) {
animation.duration = group.duration;
/// 生成 values 数组,(动画过程)
[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 函数首先把锚点放在中心,然后执行 刚才标记的 A 处的block(生成JHKeyframeAnimation 对象,并且放入 动画数组中)
-(void) chainLinkDidFinishAnimating {
/// 删除已经执行过的动画
[self.JHAnimationCompletionActions removeObjectAtIndex:0];
[self.JHAnimationCalculationActions removeObjectAtIndex:0];
[self.JHAnimations removeObjectAtIndex:0];
/// 删除已经执行过的group
[self.JHAnimationGroups removeObjectAtIndex:0];
[self sanityCheck];
/// 如果响应连中没有要执行的动画了就清空
if (self.JHAnimationGroups.count == 0) {
[self clear];
if (self.animationCompletion) {
JHAnimationCompletion completion = self.animationCompletion;
self.animationCompletion = nil;
completion();
}
}
else {
/// 如果响应链中还有要执行的动画,继续调用
[self animateChain];
}
}
chainLinkDidFinishAnimating
一组动画执行完毕后,结束的调用。
- 首先会删除已经执行过的动画。
- 然后检查一下剩余动画group的的数量是否合法。
- 如果有剩余的动画组没执行,则继续调用
animateChain
,如果没有,则调用animationCompletion
block
下面代码过程就是根据 JHKeyframeAnimation 的 fromValue 和 toValue 计算出一组值,赋值给 values对象。
-(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];
}
valueArrayForStartValue
计算出一组值赋值给values