Home > News content

Facebook POP,迈向大师操作之路

via:博客园     time:2014/6/4 18:30:08     readed:1396

Facebook 发布 Paper 之后,似乎还不满足于只是将其作为一个概念性产品,反而更进一步开源了其背后的动画引擎 POP(https://github.com/facebook/pop),此举大有三年前发布的 iOS UI 框架 Three20(https://github.com/facebookarchive/three20)的意味。而 POP 开源后不负 Facebook 的厚望,在 GitHub 上不足一个月的时间,就已拥有了 6000 多个 Star,非常火爆。

POP 背后的开发者是 Kimon Tsinteris,他是 Push Pop Press 的联合创始人,曾在苹果担任高级工程师,并参与了 iPhone 和 iPad 上的软件研发(iPhone 的指南针及地图)。2011 年,Facebook 收购了他的公司,此后他便加入了 Facebook 负责 Facebook iOS 版本的开发。

如果你打开 Push Pop Press 开发的《AI Gore》这款 App,就会发现它的交互和动画与 Paper 几乎如出一辙,原因就在于,它们都是由 Kimon Tsinteris 开发的。由于不满足于苹果自身动画框架的单调,Push Pop Press 致力于创造一个逼真的、充满物理效应的体验。POP 就是在这个理念下催生出来的新一代成果。

POP 使用 Objective-C++编写。Objective-C++是对 C++ 的扩展,就像 Objective-C 是C的扩展一样。而至于为什么他们用 Objective-C++而不是纯粹的 Objective-C,原因在于他们更喜欢 Objective-C++的语法特性所提供的便利。

POP 的架构

POP 目前由四个部分组成(如图 1 所示),即 Animations、Engine、Utility、WebCore。

图 1 POP 架构图

POP 动画极为流畅,其秘密就在于这个引擎中的 POPAnimator。POP 通过 CADisplayLink 让动画实现了 60 FPS 的流畅效果,打造了一个游戏级的动画引擎。

CADisplayLink 是类似 NSTimer 的定时器,不同之处在于,NSTimer 用于我们定义任务的执行周期及资料的更新周期,它的执行受 CPU 的阻塞所影响。而 CADisplayLink 则用于定义画面的重绘和动画的演变,它的执行是基于 Frames 的间隔。通过 CADisplayLink,苹果允许开发者将 App 的重绘速度设定到与屏幕刷新频率一致。因此开发者可以获得非常流畅的交互动画,这项技术的应用在游戏中非常常见,著名的 Cocos-2d 引擎也用到了这个重要的技术。

WebCore 里包含了一些从苹果的开源的网页渲染引擎里拿到的源文件(http://opensource.apple.com/source/WebCore),它与 Utility 里的组件一并为 POP 的各项复杂计算提供了基本支持。因此,通过 Engine、Utility、WebCore 三个基石,打造了 Animations。

POPAnimation 有着与 CALayer 非常相似的 API。如果你知道 CALayer 的动画 API,那么你对下面的接口一定非常熟悉。说到这里,想必你一定开始迫不及待地想试试 POP 了(因篇幅所限,下面的代码并不是完整代码,你可以到 https://github.com/kevinzhow/pop-handapp 获取示例 App)。

基本类型

· Spring Animation

图 2 默认的两种动画模式以及他们的动画节奏

POP 默认提供了两个非常特别的动画模式,第一个就是 Spring Animation(如图 2 所示),另一个是 Decay Animation。让我们先来看看 Spring Animation,控制其动画效果的主要参数包括:

· Bounciness 反弹,影响动画作用的参数的变化幅度;

· Speed 速度;

· Tension 拉力,影响回弹力度及速度;

· Friction 摩擦力,开启后,动画会不断重复,并且幅度逐渐削弱,直到停止;

· Mass 质量,细微地影响动画的回弹力度和速度。

实际上,Tension、Friction、Mass 这三个参数的作用很微妙,需要在示例程序中仔细体会。使用 Spring Animation 的方式非常简单,如代码 1 所示。

代码1

通过[POPSpringAnimation animationWithPropertyNamed:kPOPLayerScaleXY]我们创建了一个在二维平面上分别沿着X轴和Y轴进行缩放的动画。

下面我们介绍三个重要的参数。

· fromValue 将告诉 POP 物体被动画操作的属性从什么数值开始运行。如果不提供 fromValue,那么 POP 将默认使用当前数值。在这个例子中,就默认使用当前的比例。

· toValue 是我们希望动画结束后,物体被动画操作的属性停留在什么值上,在这个例子中,toValue 告诉了 POP,我们希望沿着X轴和Y轴各缩放几倍。

· completionBlock 提供了一个 Callback,动画的执行过程会不断调用这个 block,finished 这个布尔变量可以用来做动画完成与否的判断。

值得一提的是,这里 toValue 和 fromValue 的值应该和动画所作用的属性是一样的数据结构。例如,如果我们的操作对象是 bounds,那么这里的 toValue 则应该是[NSValue valueWithCGRect:] 。

最后,我们使用 pop_addAnimation 来让动画开始生效,如果想删除动画的话,那么需要调用 pop_removeAllAnimations。

与 iOS 自带的动画不同,如果你在动画的执行过程中删除了物体的动画,那么物体会停在动画状态的最后一个瞬间,而不是闪回开始前的状态。

· Decay Animation

Decay Animation 可以实现衰减的动画效果。这个动画有一个重要的参数即 velocity(速率),这个参数一般并不用于物体的自发动画,而是与用户的交互共生。这和 iOS 7 引入的 UIDynamic 非常相似,如果你想实现一些物理效果,Decay Animation 也是不错的选择。

Decay 的动画没有 toValue 只有 fromValue,以 fromValue 作为原始值,按照 velocity 来做衰减操作。如果我们想做一个刹车效果,则可以像代码 2 这样操作:

代码2

这个动画会使得物体从X坐标的 25.0 开始做 100 点/秒的减速运动。如果 velocity 里的数字是负值,那么你的动画就会反方向执行动画效果。这里非常值得一提的是,velocity 也是必须和你操作的属性有相同的数据结构,如果你操作的是 bounds,想实现一个水滴滴到桌面的扩散效果,那么 velocity 则应该是[NSValue valueWithRect:CGRectMake (0, 0, 20, 20)]。

deceleration(负加速度)是一个很少用到的值,它影响动画被重力影响的效果。默认值就是我们地球的重力加速度 0.998。如果你程序里的动画开发给火星人看,那么使用 0.376 这个值会更合适。

· Property Animation 和 Basic Animation

POP 号称可以对物体的任何属性进行动画,其背后就是这个 Property Animation 驱动。Spring Animation 和 Decay Animation 都是继承自这个类,接下来我们通过一个 Counting Label 的例子来演示 Property Animation 的神奇能力。在这个动画中,我们也使用了 Basic Animation,动画模式是经典的 ease-in-out,不使用 Spring Animation 是因为我们并不需要计数器的数值进行回弹,如代码 3 所示。

代码3

通过 POPBasicAnimation 的 timingFunction 我们定义了动画的展现方式——渐入渐出。随后通过 POPAnimatableProperty 来定义 POP 如何操作 Label 上的数值。

这里我们需要注意两个函数,readBlock 和 writeBlock。readBlock 定义了动画如何获取要操作的属性数值,writeBlock 定义了动画如何修改要操作的属性数值。在这两个函数中,obj 就是我们的 Label,values 是动画所操作的属性数组,其值必须是 CGFloat。

你可能会问,什么是动画所操作的属性数组?回顾之前我们在 Decay Animation 中操作的 bounds 内容,可以看出 values[0]、values[1]、values[2]、values[3]分别对应了 CGRectMake(0, 0, 20.0, 20.0)的0、0、20.0、20.0。这里我们需要操作 Label 上显示的文字,所以只需要一个 values[0]属性即可。

通过 values[0]=[[obj description] floatValue]我们告诉 POP 如何获取这个值。相应地,我们通过[obj setText:[NSString stringWithFormat:@"%.2f",values[0]]],告诉 POP 如何改变 Label 的属性。

threshold 定义了动画的变化阀值,如果这里使用1,那么我们就不会看到动画执行时小数点后面的数字变化。

到这里,我们的 Counting Label 就完成了,是不是超简单?

实战

· PopUp 和 Decay Move

这个实例中,我将介绍一下如何将 Decay 动画和用户的手势操作结合起来,实现一个推冰壶的效果。手势的处理方式如代码 4 所示。

代码4

当用户触摸这个冰壶时,所有动画会立刻停止,然后冰壶会跟随用户的手指移动。在用户松开冰壶时,通过[pan velocityInView:self.view]我们获取了用户手指移动的速率,在 addDecayPositionAnimationWithVelocity 中生成动画,如代码 5 所示。

代码5

动画生效后,冰壶就会在低摩擦的状态下前进并逐渐停止。如果想增大摩擦力,则可以将速率乘以摩擦系数。

· Fly In

在这个实例中,我将介绍一下如何将两个动画相结合,实现一个像 Path 中卡片飞入的效果。如代码 6 所示。

代码6

第一个 Spring Animation 实现了卡片下落的效果,第二个 Basic Animation 实现了卡片的渐入效果,而最后的一个 Basic Animation 则实现了卡片倾斜的效果。

这里需要注意的是,我们使用了 duration 来定义 Basic Animation 的执行时间,并用 beginTime 来定义动画的开始时间。beginTime 接受的是一个以秒为单位的时间,所以我们使用了 CACurrentMediaTime ()来获取当前的时间,在此之上增加上了期望动画延迟的时间。

· Transform

这个实例真的酷极了,我们将实现一个用户点击后播放按钮转换为进度条容器的变形效果。首先创建一个进度条,通过 lineCap lineWidth 调整进度条的样式,然后使用 UIBezierPath 来定义进度条的走向,如代码 7 所示。

代码7

代码 8 就是实现变形的代码。从这段代码不难看出,scale 和 bounds 的变化效果是一起进行的。这时,播放按钮将缩小,然后改变外形成为进度条的容器。在变形结束后,将触发进度条的动画。

代码8

这里我们使用 UIGraphicsBeginImageContext-WithOptions ()去开启绘画上下文,动画结束后使用 UIGraphicsEndImageContext ()来清空绘画的上下文。这两个函数主要是影响画板的大小。

作者周楷雯,广州趣拼科技创始人。钟情 iOS,粗通 Rails,偶尔做做设计,总是被有趣的人和美好的事所吸引。

China IT News APP

Download China IT News APP

Please rate this news

The average score will be displayed after you score.

Post comment

Do not see clearly? Click for a new code.

User comments