前言
浏览的时候一篇发现仿微博的功能菜单,实现原理介绍的很详细,demo里面使用的是Facebook的pop动画库添加的动画效果,感觉很不错,于是创建了个demo,利用pop动画库,写写这种效果.
首先简单介绍下Facebook的pop Animation
POP: 一个流行的可扩展的动画引擎iOS,它支持spring和衰变动态动画,使其可用于构建现实,基于物理交互。Objective - C API允许快速集成, 对于所有的动画和过渡他是成熟的.
POP 目前由四部分组成:1. Animations;2. Engine;3. Utility;4. WebCore。
下图有助于你更好的理解它的架构
POP 动画极为流畅,其秘密就在于这个 Engine 中的POPAnimator 里,POP 通过 CADisplayLink 高达 60 FPS 的特性,打造了一个游戏级的动画引擎。
CADisplayLink 是类似 NSTimer 的定时器,不同之处在于,NSTimer 用于我们定义任务的执行周期、资料的更新周期,他的执行受到 CPU 的阻塞影响,而 CADisplayLink 则用于定义画面的重绘、动画的演变,他的执行基于 frames 的间隔。
通过 CADisplayLink,Apple 允许你将 App 的重绘速度设定到和屏幕刷新频率一致,由此你可以获得非常流畅的交互动画,这项技术的应用在游戏中非常常见,著名的 Cocos-2D 也应用了这个重要的技术。
WebCore 里包含了一些从 Apple 的开源的网页渲染引擎里拿出的源文件,与 Utility 里的组件一并,提供了 POP 的各项复杂计算的基本支持。
由此通过 Engine、Utility、WebCore 三个基石,打造了Animations。
POPAnimation 有着和 CALayer 非常相似的 API。如果你知道 CALayer 的动画 API,那么你对下面的接口一定非常熟悉, 想必你一定开始迫不及待想试试 POP 了,我们现在就 Jump right in。
动画菜单的核心代码如下:
- (void)startButtonAnimation
{
for (NSInteger i = 0 ; i < self.buttonImages.count ; i++) {
CGFloat buttonW = SCREEN_W / 3;
CGFloat buttonX = i % 3 * buttonW;
CGFloat buttonY = 200 + i / 3 * buttonW;
LSPopAnimationButton *button = [LSPopAnimationButton buttonWithType:UIButtonTypeCustom];
[button setImage:[UIImage imageNamed:self.buttonImages[i]] forState:UIControlStateNormal];
[button setTitle:self.buttonTitles[i] forState:UIControlStateNormal];
[button setTitleColor:[UIColor blackColor] forState:UIControlStateNormal];
button.titleLabel.textAlignment = NSTextAlignmentCenter;
button.titleLabel.font = [UIFont systemFontOfSize:15];
[self.view addSubview:button];
[self.buttons addObject:button];
// 动画
POPSpringAnimation *anim = [POPSpringAnimation animationWithPropertyNamed:kPOPViewFrame];
anim.fromValue = [NSValue valueWithCGRect:CGRectMake(buttonX, buttonY - SCREEN_H, buttonW, buttonW)];
anim.toValue = [NSValue valueWithCGRect:CGRectMake(buttonX, buttonY, buttonW, buttonW)];
anim.springSpeed = 10;
anim.springBounciness = 10;
// CACurrentMediaTime()获得的是当前时间
anim.beginTime = CACurrentMediaTime() + [self.times[i] doubleValue];
[button pop_addAnimation:anim forKey:nil];
}
}
- (void)startTipImageViewAnimation
{
_tipImageView = [[UIImageView alloc] initWithImage:[UIImage imageNamed:@"app_slogan"]];
CGFloat imageX = (SCREEN_W - _tipImageView.image.size.width) / 2;
CGFloat imageY = 160 - _tipImageView.image.size.height;
CGFloat imageW = _tipImageView.image.size.width;
CGFloat imageH = _tipImageView.image.size.height;
_tipImageView.y = imageY - SCREEN_H;
[self.view addSubview:_tipImageView];
// 动画
POPSpringAnimation *anim = [POPSpringAnimation animationWithPropertyNamed:kPOPViewFrame];
anim.fromValue = [NSValue valueWithCGRect:CGRectMake(imageX, imageY - SCREEN_H, imageW, imageH)];
anim.toValue = [NSValue valueWithCGRect:CGRectMake(imageX, imageY, imageW, imageH)];
anim.springSpeed = 10;
anim.springBounciness = 10;
// CACurrentMediaTime()获得的是当前时间
anim.beginTime = CACurrentMediaTime() + [self.times.lastObject doubleValue];
[_tipImageView pop_addAnimation:anim forKey:nil];
}
- (NSArray *)times
{
if (!_times) {
CGFloat interval = 0.1; // 时间间隔
_times = @[@(5 * interval),
@(4 * interval),
@(3 * interval),
@(2 * interval),
@(0 * interval),
@(1 * interval),
@(6 * interval)]; // 标语的动画时间
}
return _times;
}
用的是POPSpringAnimation动画,对frame中Y值进行动画,设置动画要添加到的视图的始末位置,设置弹性速度:springSpeed和弹性值:springBounciness.设置开始时间(因为有六个按钮和一个标语要进行动画,所以开始时间要有先后顺序),pop_addAnimation添加动画到控件即可.若要对动画进行移除操作,可以定义一个key对动画进行标记.
下面介绍下 Core Animation和pop Animation的不同点:
Core Animation 工作机制
首先我们需要了解CA是如何工作的。每当我们创建并添加动画到 layer 时,QuartzCore 框架就会把动画的参数打包好,然后通过 IPC (处理器)发送给名为 backboardd 的后台处理程序。你的应用也会发送当前展示在屏幕上的每一个 layer 的信息。
backboardd 会处理 layer 的结构体系然后通过 OpenGL 绘制出来。它还会处理你已经添加过的动画(也可以是视图,因为视图本质是包裹着 layer的)。你一定要理解的是,backboardd 使得动画的每一帧都可以在你的应用中完全独立。这里唯一的回调是动画的开始和结束(详见CAAnimationDelegate 协议)。你的应用完全不会参与动画的绘制,这些绘制完全独立于你的应用进程(除非你明确地在你的应用中通过动画通用属性要求绘制动画帧)。这意味着你可以继续在主线程做其他事情,并且不会影响到 CAAnimation 的性能。如果你阻塞了你的主线程,或者你在调试器中暂停了你的程序,你的动画还是会继续执行。
POP 工作机制
现在有很多优秀的第三方动画库,POP 因为其使用灵活、功能强大、文档齐全,所以备受好评,先看一下官方介绍:
POP是一个在iOS与OS X上通用的极具扩展性的动画引擎 它在基本的静态动画的基础上增加的弹簧动画与衰减动画。
使之能创造出更真实更具物理性的交互动画 POP的API可以快速的与现有的ObjC代码集成并可以作用于任意对象的任意属性。
POP是个相当成熟且久经考验的框架 Facebook出品的令人惊叹的Paper应用中的所有动画和效果即出自POP。
更为详细的介绍和使用请查看官方文档以及里脊串的 POP介绍与使用实践(快速上手动画)。
POP 本质上是基于定时器的动画库,使用每秒 60 频率的定时器,即时钟频率为 1/60 秒(为了匹配 iOS 显示屏帧率),使得动画刷新绘制频率与屏幕刷新频率一致。很多这类动画库都使用 CADisplayLink 做为一个回调源。
一旦定时器刷新,动画库计算动画的进程,这意味着动画库会计算那些活动的东西的状态(通常是layer 属性,如 bound,opactiy,transform 等)。然后动画库提供最新计算的值给有动画的 layer (或者其他对象)。最主要的区别是,layer 的状态将会在这种情况下改变。
由于 layer 的一些参数已经被改变,你的应用必须通过 IPC 通知 backboardd 处理这些变化。当 backboardd 接收到变化通知(同时接收到的还有应用中的 layer 树),它将在屏幕上重绘一切东西。这意味着,你应用中做的每一个动画帧都会传送数据到 backboardd (即通知 backboardd ),因为 backboardd 完全不知道 layer 发生了什么事情。综上,你的应用就是在这种情况下运行动画的。
Core Animation 和 POP 运行动画对比
由于 POP 是基于定时器定时刷新添加动画的原理,那么如果将动画库运行在主线程上,会由于线程阻塞的问题导致动画效果出现卡顿、不流畅的情况。更为关键的是,你不能将动画效果放在子线程,因为你不能将对 view 和 layer 的操作放到主线程之外。
参考地址:
http://www.jianshu.com/p/412f71f8f2d1
详细介绍pop动画博客地址:
http://www.cocoachina.com/ios/20140527/8565.html
http://www.cocoachina.com/ios/20140704/9034.html
http://www.cocoachina.com/ios/20151223/14739.html
demo地址: https://github.com/liuyunxin2014/Facebook-popAnimation.git