pop动画框架源码初步探究

pop架构

由四大部分组成

1.Animations

2.Engine

3.Utility

4.WebCore

第一部分Animations

这一部分当中定义了动画的类型,一共4种,分别是:

POPBasicAnimation        //基本动画固定时间间隔

POPCustomAnimation    //自定义动画

POPDecayAnimation      //带阻尼动画效果

POPSpringAnimation     //弹簧动画效果

各自有不同的使用场景

再看一下此目录中的类继承的结构关系

POPAnimation

———> POPPropertyAnimation

———>POPBasicAnimation

———>POPDecayAnimation

———>POPSpringAnimation

———>POPCustomAnimation

POPAnimation中定义了各种动画中需要的公有属性

name beginTime tracer block 以及相应的代理回调,类扩展等等,以及引用了POPAnimationInternal中的私有结构体等(_POPAnimationState)

basic动画示例

spring动画

想了下,写这些东西网上随便都能查的到啊,这样还是看看源码pop如何具体实现一个动画效果的,所以请抛开上面所说的Animations框架主题

先从一个最基本的basic动画的创建开始

1. POPBasicAnimation *anBasic = [POPBasicAnimation animationWithPropertyNamed:kPOPLayerPositionX];

2. anBasic.toValue = @(testView.center.y+50);

3. anBasic.beginTime = CACurrentMediaTime()+2.0f;

4. [testView pop_addAnimation:anBasic forKey:@"position"];

先看一下代码的执行流程

POPBasicAnimation *anBasic = [POPBasicAnimation animationWithPropertyNamed:kPOPLayerPositionX];

然后进入到这个方法

1.

+ (instancetype)animationWithPropertyNamed:(NSString*)aName

{

POPBasicAnimation*anim = [selfanimation];

anim.property= [POPAnimatable PropertypropertyWithName:aName];//跳入2中

return anim;

}

(POPBasicAnimation)

2.

+ (id)propertyWithName:(NSString*)aName

{

return[self propertyWithName:aNameinitializer:NULL];//跳入3中

}

(POPAnimatableProperty)

3.

/**

* 返回要进行的动画属性

*

*  @param aName  属性名称

*  @param aBlock

*

*  @return <#return value description#>

*/

+ (id)propertyWithName:(NSString*)aName initializer:(void(^)(POPMutableAnimatableProperty*prop))aBlock

{

POPAnimatableProperty*prop =nil;//声明一个空的POPAnimatableProperty对象

staticNSMutableDictionary*_propertyDict =nil;

if(nil== _propertyDict) {

_propertyDict = [[NSMutableDictionary alloc]initWithCapacity:10];//实例化一个静态可变字典

}

prop = _propertyDict[aName];

if(nil!= prop) {

returnprop;//如果prop不等于nil则返回prop

}

NSUIntegerstaticIdx =staticIndexWithName(aName);//如果prop为nil,则通过此函数定位下标

if(NSNotFound!= staticIdx) {

//如果下标不为NSNotFound,则新建一个POPStaticAnimatableProperty对象

POPStaticAnimatableProperty*staticProp = [[POPStaticAnimatableProperty alloc]init];

//让新建对象的结构体指向通过下标根据结构体数组找到的结构体(语言组织有点问题)

staticProp->_state= &_staticStates[staticIdx];

//添加字典中的key-value,key是aName,Value是POPStaticAnimatableProperty对象

_propertyDict[aName] = staticProp;

prop = staticProp;

}else if(NULL!= aBlock) {

//如果block参数不为空,则创建POPMutableAnimatableProperty对象

POPMutableAnimatableProperty*mutableProp =[[POPMutableAnimatableProperty alloc]init];

mutableProp.name= aName;

mutableProp.threshold=1.0;

aBlock(mutableProp);执行block

prop = [mutableProp copy];

}

return prop;

}

接着

anBasic.toValue = @(testView.center.y+50);

执行

- (void)setToValue:(id)aValue

{

POPPropertyAnimationState*s =__state;

VectorRefvec =POPUnbox(aValue, s->valueType, s->valueCount,YES);//解包一个向量,把point,size ,rect,color对象转换为vector

//vector的各种逻辑判断

if(!vec_equal(vec, s->toVec)) {

s->toVec= vec;

// invalidate to dependent state

s->didReachToValue=false;

s->distanceVec=NULL;

if(s->tracing) {

//把动画过程中的属性变化加入追踪器,生成event加入events数组中

[s->tracerupdateToValue:aValue];

}

// automatically unpause active animations

if(s->active&& s->paused) {

s->setPaused(false);

}

}

}

最后看一下

[testViewpop_addAnimation:anBasicforKey:@"position"];

是如何把一个animation加到view上一个动画就能够动起来

先看一下都调用了哪些方法

- (void)pop_addAnimation:(POPAnimation*)anim forKey:(NSString*)key

{

[[POPAnimator sharedAnimator]addAnimation:anim forObject:self key:key];

}

继续...

+ (id)sharedAnimator

{

staticPOPAnimator* _animator =nil;

staticdispatch_once_tonceToken;

dispatch_once(&onceToken, ^{

_animator = [[POPAnimator alloc] init];

});

return_animator;

}

继续

- (id)init

{

self= [super init];

if(nil==self)return nil;

#if TARGET_OS_IPHONE

_displayLink= [CADisplayLink displayLinkWithTarget:self selector:@selector(render)];

//这个时候为pause状态为yes动画还没开始跑

_displayLink.paused=YES;

[_displayLink addToRunLoop:[NSRunLoop mainRunLoop]forMode:NSRunLoopCommonModes];

#else

CVDisplayLinkCreateWithActiveCGDisplays(&_displayLink);

CVDisplayLinkSetOutputCallback(_displayLink, displayLinkCallback, (__bridgevoid*)self);

#endif

_dict=POPDictionaryCreateMutableWeakPointerToStrongObject(5);

_lock=OS_SPINLOCK_INIT;

returnself;

}

看见没在这里创建了一个CADisplayLink对象(关于CADisplayLink是个什么东西,可以自行google)

重点来了,这里把_displayLink对象加入到主线程,然后调用render方法

再看一下之后调用的

- (void)addAnimation:(POPAnimation*)anim forObject:(id)obj key:(NSString*)key

{

//判断为空则返回

if(!anim || !obj) {

return;

}

// support arbitrarily many nil keys

if(!key) {

key = [[NSUUID UUID]UUIDString];

}

// lock

OSSpinLockLock(&_lock);

// get key, animation dict associated with object

// 把之前init方法中创建的_dict赋给keyAnimationDict

NSMutableDictionary*keyAnimationDict = (__bridgeid)CFDictionaryGetValue(_dict, (__bridgevoid*)obj);

// update associated animation state

if(nil== keyAnimationDict) {

//判断为空的情况下的处理

keyAnimationDict = [NSMutableDictionary dictionary];

CFDictionarySetValue(_dict, (__bridgevoid*)obj, (__bridgevoid*)keyAnimationDict);

}else{

// if the animation instance already exists, avoid cancelling only to restart

POPAnimation*existingAnim = keyAnimationDict[key];//根据所传入的key查找animation

if(existingAnim) {

// unlock

OSSpinLockUnlock(&_lock);

if(existingAnim == anim) {

//如果此查找到的existingAnim等于传入的anim则返回

return;

}

这里是应该防止,多个不同的animation公用一个key值

[selfremoveAnimationForObject:objkey:keycleanupDict:NO];

// lock

OSSpinLockLock(&_lock);

}

}

keyAnimationDict[key] = anim;

// create entry after potential removal

POPAnimatorItemRefitem(new POPAnimatorItem(obj, key, anim));

// add to list and pending list

_list.push_back(item);

_pendingList.push_back(item);

// support animation re-use, reset all animation state

POPAnimationGetState(anim)->reset(true);

// update display link

这个时候呢 根据方法实现给_displayLink.paused= NO;了 开始跑了

updateDisplayLink(self);

// unlock

OSSpinLockUnlock(&_lock);

// schedule runloop processing of pending animations

[self_scheduleProcess PendingList];

}

最后进入- (void)_scheduleProcessPendingList

- (void)_scheduleProcessPendingList

{

// see WebKit for magic numbers, eghttp://trac.webkit.org/changeset/166540

static const CFIndexCATransactionCommitRunLoopOrder =2000000;

static const CFIndexPOPAnimationApplyRunLoopOrder = CATransactionCommitRunLoopOrder -1;

// lock

OSSpinLockLock(&_lock);

if(!_pendingListObserver) {

__weakPOPAnimator*weakSelf =self;

//监听runloop

_pendingListObserver=CFRunLoopObserverCreateWithHandler(kCFAllocatorDefault,kCFRunLoopBeforeWaiting|kCFRunLoopExit,false, POPAnimationApplyRunLoopOrder, ^(CFRunLoopObserverRefobserver,CFRunLoopActivityactivity) {

[weakSelf _processPendingList];//kCFRunLoopBeforeWaiting|kCFRunLoopExit下回调清除动画

});

if(_pendingListObserver) {

添加到主线程的runloop中

CFRunLoopAddObserver(CFRunLoopGetMain(),_pendingListObserver,kCFRunLoopCommonModes);

}

}

// unlock

OSSpinLockUnlock(&_lock);

}

然后呢_displaylink就开始调用rend方法进行动画了,就开始以1秒60次的频率刷新UI

整个过程就是这么一个过程

推荐阅读更多精彩内容