iOS可以通过CADisplayLink实现自定义动画引擎,pop就是基于此实现的,而且比原生Core Animation更强大好用。譬如当ViewController侧滑返回的时候,系统会将Core Animation的动画会停止,而基于CADisplayLink实现的动画则不会停止,因而可以实现类似网易云音乐从播放页侧滑时hold住专辑封面图旋转的效果。

八一八魔性的pop

1、实用的宏

#define POP_ARRAY_COUNT(x) sizeof(x) / sizeof(x[0])

#define FB_PROPERTY_GET(stype, property, ctype) \

- (ctype)property { \

return ((stype *)_state)->property; \

}

#define FB_PROPERTY_SET(stype, property, mutator, ctype, ...) \

- (void)mutator (ctype)value { \

if (value == ((stype *)_state)->property) \

return; \

((stype *)_state)->property = value; \

__VA_ARGS__ \

}

#define FB_PROPERTY_SET_OBJ_COPY(stype, property, mutator, ctype, ...) \

- (void)mutator (ctype)value { \

if (value == ((stype *)_state)->property) \

return; \

((stype *)_state)->property = [value copy]; \

__VA_ARGS__ \

}

2、判定值的数据类型

pop定义了支持的值的数据类型

const POPValueType kPOPAnimatableSupportTypes[10] = {kPOPValueInteger, kPOPValueFloat, kPOPValuePoint, kPOPValueSize, kPOPValueRect, kPOPValueEdgeInsets, kPOPValueColor, kPOPValueSCNVector3, kPOPValueSCNVector4};

通过@encode指令,将给定类型编码的内部字符串与objcType对比,得到值的数据类型

static bool FBCompareTypeEncoding(const char *objctype, POPValueType type)

{

switch (type)

{

case kPOPValueFloat:

return (strcmp(objctype, @encode(float)) == 0

|| strcmp(objctype, @encode(double)) == 0

);

case kPOPValuePoint:

return (strcmp(objctype, @encode(CGPoint)) == 0

#if !TARGET_OS_IPHONE

|| strcmp(objctype, @encode(NSPoint)) == 0

#endif

);

case kPOPValueSize:

return (strcmp(objctype, @encode(CGSize)) == 0

#if !TARGET_OS_IPHONE

|| strcmp(objctype, @encode(NSSize)) == 0

#endif

);

case kPOPValueRect:

return (strcmp(objctype, @encode(CGRect)) == 0

#if !TARGET_OS_IPHONE

|| strcmp(objctype, @encode(NSRect)) == 0

#endif

);

case kPOPValueEdgeInsets:

#if TARGET_OS_IPHONE

return strcmp(objctype, @encode(UIEdgeInsets)) == 0;

#else

return false;

#endif

case kPOPValueAffineTransform:

return strcmp(objctype, @encode(CGAffineTransform)) == 0;

case kPOPValueTransform:

return strcmp(objctype, @encode(CATransform3D)) == 0;

case kPOPValueRange:

return strcmp(objctype, @encode(CFRange)) == 0

|| strcmp(objctype, @encode (NSRange)) == 0;

case kPOPValueInteger:

return (strcmp(objctype, @encode(int)) == 0

|| strcmp(objctype, @encode(unsigned int)) == 0

|| strcmp(objctype, @encode(short)) == 0

|| strcmp(objctype, @encode(unsigned short)) == 0

|| strcmp(objctype, @encode(long)) == 0

|| strcmp(objctype, @encode(unsigned long)) == 0

|| strcmp(objctype, @encode(long long)) == 0

|| strcmp(objctype, @encode(unsigned long long)) == 0

);

case kPOPValueSCNVector3:

#if SCENEKIT_SDK_AVAILABLE

return strcmp(objctype, @encode(SCNVector3)) == 0;

#else

return false;

#endif

case kPOPValueSCNVector4:

#if SCENEKIT_SDK_AVAILABLE

return strcmp(objctype, @encode(SCNVector4)) == 0;

#else

return false;

#endif

default:

return false;

}

}

3、将值的数据类型标准化为Vector

举个CGRect类型的例子:

case kPOPValueRect:

vec = Vector::new_cg_rect([value CGRectValue]);

Vector *Vector::new_cg_rect(const CGRect &r)

{

Vector *v = new Vector(4);

v->_values[0] = r.origin.x;

v->_values[1] = r.origin.y;

v->_values[2] = r.size.width;

v->_values[3] = r.size.height;

return v;

}

通过Vector的两个参数size_t _count;、CGFloat *_values;将给定的类型抽象出来,实现解耦。此外还有一个好处,当创建属性动画为kPOPLayerBounds,但toValue属性赋值的是一个NSNumber,得益于_values是数组指针,并不会引发数组越界导致的crash,只是动画效果不可预期。

4、基于NSRunLoop的动画更新机制

- (void)_scheduleProcessPendingList

{

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

static const CFIndex CATransactionCommitRunLoopOrder = 2000000;

static const CFIndex POPAnimationApplyRunLoopOrder = CATransactionCommitRunLoopOrder - 1;

// lock

OSSpinLockLock(&_lock);

if (!_pendingListObserver) {

__weak POPAnimator *weakSelf = self;

_pendingListObserver = CFRunLoopObserverCreateWithHandler(kCFAllocatorDefault, kCFRunLoopBeforeWaiting | kCFRunLoopExit, false, POPAnimationApplyRunLoopOrder, ^(CFRunLoopObserverRef observer, CFRunLoopActivity activity) {

[weakSelf _processPendingList];

});

if (_pendingListObserver) {

CFRunLoopAddObserver(CFRunLoopGetMain(), _pendingListObserver,  kCFRunLoopCommonModes);

}

}

// unlock

OSSpinLockUnlock(&_lock);

}

在主线程RunLoop中添加观察者,监听了kCFAllocatorDefault、kCFRunLoopBeforeWaiting、kCFRunLoopExit事件,在收到回调的时候,处理_pendingList里的动画。

5、更新动画的回调数组

static POPStaticAnimatablePropertyState _staticStates[] =

{

/* CALayer */

{kPOPLayerBackgroundColor,

^(CALayer *obj, CGFloat values[]) {

POPCGColorGetRGBAComponents(obj.backgroundColor, values);

},

^(CALayer *obj, const CGFloat values[]) {

CGColorRef color = POPCGColorRGBACreate(values);

[obj setBackgroundColor:color];

CGColorRelease(color);

},

kPOPThresholdColor

},

{kPOPLayerBounds,

^(CALayer *obj, CGFloat values[]) {

values_from_rect(values, [obj bounds]);

},

^(CALayer *obj, const CGFloat values[]) {

[obj setBounds:values_to_rect(values)];

},

kPOPThresholdPoint

},

...

封装不同的动画行为,实现类似模板模式,只需统一调用,即可更新动画

// write value

write(obj, currentVec->data());

6、动画插值的动态实现

switch (type) {

case kPOPAnimationSpring:

advanced = advance(time, dt, obj);

break;

case kPOPAnimationDecay:

advanced = advance(time, dt, obj);

break;

case kPOPAnimationBasic: {

advanced = advance(time, dt, obj);

computedProgress = true;

break;

}

case kPOPAnimationCustom: {

customFinished = [self _advance:obj currentTime:time elapsedTime:dt] ? false : true;

advanced = true;

break;

}

default:

break;

}

可以看出总共有四种动画插值的算法,以kPOPAnimationBasic为例:

bool advance(CFTimeInterval time, CFTimeInterval dt, id obj) {

// default timing function

if (!timingFunction) {

((POPBasicAnimation *)self).timingFunction = [CAMediaTimingFunction functionWithName:kCAMediaTimingFunctionDefault];

}

// solve for normalized time, aka progresss [0, 1]

CGFloat p = 1.0f;

if (duration > 0.0f) {

// cap local time to duration

CFTimeInterval t = MIN(time - startTime, duration) / duration;

p = POPTimingFunctionSolve(timingControlPoints, t, SOLVE_EPS(duration));

timeProgress = t;

} else {

timeProgress = 1.;

}

// interpolate and advance

interpolate(valueType, valueCount, fromVec->data(), toVec->data(), currentVec->data(), p);

progress = p;

clampCurrentValue();

return true;

}

依照给定的timingFunction,使用POPTimingFunctionSolve计算贝塞尔曲线的变化率,再通过混合计算#define MIX(a, b, f) ((a) + (f) * ((b) - (a))),最终得到动画的插值。

小结

pop中还有很多有意思的地方,譬如TransformationMatrix里的矩阵操作,这里就暂且不挖WebCore底层了。简而言之,无论性能(c++混编)、易用、容错,pop都有着作为引擎该有的特性,而它所暴露的和Core Animation相似的接口也让人极易上手!

聊聊动画引擎 pop的更多相关文章

  1. POP动画引擎中Layer与CALayer的一点区别

    POP动画引擎是facebook提供的一个开源框架, 可以实现很多的动画效果, 这里就不一一介绍啦, 有兴趣的童鞋请移步: https://github.com/facebook/pop 下面简单的讲 ...

  2. iOS动画——弹窗动画(pop动画)

    用pop动画简单实现弹窗的缩放和渐变,感觉这个动画常用,就写一下博客 pop动画是Facebook推出的动画引擎,请自行到GitHub上搜索下载拖拽导入xcode项目中. 更多pop动画使用和原理可网 ...

  3. SpriteSheet精灵动画引擎

    SpriteSheet精灵动画引擎   本文介绍Flash中SpriteSheet精灵序列图与其它渲染方式的性能对比.SpriteSheet的原理及注意实现,最后实现了一个精灵序列图的渲染引擎.本文的 ...

  4. 第四十二课:基于CSS的动画引擎

    由于低版本浏览器不支持css3 animation,因此我们需要根据浏览器来选择不同的动画引擎.如果浏览器支持css3 animation,那么就使用此动画引擎,如果不支持,就使用javascript ...

  5. [译]理解 Windows UI 动画引擎

    本文译自 Nick Waggoner 的 "Understand what’s possible with the Windows UI Animation Engine",已获原 ...

  6. 基于canvas与原生JS的H5动画引擎

    前一段时间项目组里有一些H5动画的需求,由于没有专业的前端人员,便交由我这个做后台的研究相关的H5动画技术. 通过初步调研,H5动画的实现大概有以下几种方式: 1.基于css实现 这种方式比较简单易学 ...

  7. Xcode7.3.1中通过最新的CocoaPod安装pop动画引擎

    CocoaPod是一个用ruby实现,用于方便的管理Xcode中第三方插件的管理器.用它我们可以很方便的安装和升级插件而不用担心破坏原有的项目. 而pop是一个用于实现App中动画的引擎,它是由Fac ...

  8. 用POP动画引擎实现弹簧动画(POPSpringAnimation)

    效果图: #import "ViewController.h" #import <POP.h> @interface ViewController () @proper ...

  9. 用POP动画引擎实现衰减动画(POPDecayAnimation)

    效果图: #import "ViewController.h" #import <POP.h> @interface ViewController () @end @i ...

随机推荐

  1. Android开发中一些被冷落但却很有用的类和方法

    MediaMetadataRetriever 顾名思义,就是用来获取媒体文件一些相关信息的类.包括一首歌的标题,作者,专辑封面和名称,时长,比特率等等.如果是视频的话,可以获取视频的长宽,预览图. h ...

  2. html5 高级动画精灵

    <!DOCTYPE HTML> <html lang="en-US"> <head> <meta charset="UTF-8& ...

  3. sougou输入法小技巧

  4. 深刻理解C#的传值调用和传引用调用

    传值调用和传引用调用是几乎所有主流语言都会涉及到的问题,下面我谈谈我对C#中传值调用和传引用调用的理解. 1. 一般对C#中传值调用和传引用调用的理解 如果传递的参数是基元类型(int,float等) ...

  5. 转自 z55250825 的几篇关于FFT的博文(二)

    题目大意:高精度乘法.     fft的实现貌似有很多种,咱先写的是一种递归的fft,应该算是比较快的了吧.参考了 Evil君 的代码,那个运算符重载看的咱P党泪流满面. (没想到P竟然有运算符重载咩 ...

  6. zoj 1033 与其说是搜索,不如说是枚举

    zoj 与其说是搜索,不如说是枚举,只不过是通过搜索来实现的罢了. 主要是要注意好闰年的判断,特别是要注意好一串数字的划分. 题意其实我也看了一个晚上,才渐渐的看懂. 题意: 给你一个字符串,其中包含 ...

  7. Http get,post,soap协议的区别

    转自:http://www.cnblogs.com/c2303191/articles/1107027.html 1.Http作为web服务的首选协议,具有4大优点:1)http非常简单,以纯文本(超 ...

  8. ActiveMQ的安全机制使用及其源代码分析 [转]

    ActiveMQ是目前较为流行的一款开源消息服务器.最近在项目开发中,需要为ActiveMQ开发基于IP的验证和授权机制,因此,对ActiveMQ的安全机制进行了了解,以下将介绍ActiveMQ的安全 ...

  9. ActiveMQ, RabbitMQ和ZeroMQ 选型关注点

    选择MQ时,主要关注的特性,可能就以下几个: 通信模式(是否满足业务场景): ActiveMQ: queue(producer/consumer), topic(publisher/subsriber ...

  10. Logo常用的12种颜色

    1.浅绿 #8cc540 2. 深绿 #009f5d 3. 暗蓝 #2456ab 4. 蓝色 #019fde 5. 深蓝 #007cdc 6. 深紫 #887ddd 7. 浅紫 #cd7bdd 8. ...