Archive for 2012年8月

Core Animation 初探

2012/08/17

尽管在产品中要实现不少动画效果,却还没用过 OS X 的 Core Animation。一方面是因为经常要考虑用一套 code base 兼顾 OS X 和 Windows 两个操作系统。另一方面是习惯了基于 MVC 的手工实现方法 —— 在 model 中加入描述过渡帧的变量,用 timer 定期修改变量并通知 view,view 根据变量绘制过渡帧。不过偶尔模糊地见闻一些对 Core Animation 的褒扬 —— 充分的利用 GPU,释放主线程的压力,iOS 的 graphics 完全转向 Core Animation 等等。所以最近终于抽空看了些 Core Animation 的资料 [1]。

真正阅读资料之前对简介 Core Animation 的优点一直有些疑问。我无法设想与手工 MVC 动画相比 Core Animation 如何进一步压榨 GPU 的效能。在手工 MVC 动画中,虽然控制中间过程的变量需要 CPU 计算,但是这种变量会被设计为最简形式。比如,实现旋转一个矩形的动画时 timer 修改的不会是矩形的四个顶点,而是在 model 中存储旋转角度的变量。在 view 绘制中间帧时,才会根据这个角度计算四个顶点。这时的计算并非主要由 CPU 承担,而是把旋转角度变为旋转矩阵,交给 GPU 担负各个顶点的计算。

Core Animation 文档再次印证了我的一个感觉。当你感觉一个东西表面上被宣称的优点不成立,那么这种感觉很可能是对的。如果这个东西仍然很流行,那么在宣传中肯定遗漏了这个东西的真正优点 [2],并且伴随着一些隐晦的妥协 [3]。Core Animation 的妥协在于,它并不能完全替代手工 MVC 方式的灵活性。手工 MVC 动画中 model 里描述过渡状态的变量和 view 根据这些变量计算显示过渡帧的过程可以自由定义,在 MVC 这一套原则下可以实现任何动画。Core Animation 仅仅实现了有限的预定义动画,如 layer 的旋转,透明度变化,颜色,位移。尽管 Core Animiation 预定义的动画属性相当多,而且有数种组合方式达到无限的组合结果,但是仍然不能覆盖理论上的任何动画。所以它的动画效果是「罐装 (canned)」的。

但「罐装」效果对灵活性的牺牲带来了在表面宣传中未提及的好处 —— transactional。即使是初学者也能很快的实现一个简单的手工 MVC 动画,但是一旦加入复杂的 transaction 要求,即使老手也很为难:如何处理在一个动画播放的过程中用户 cancel 或者 undo 一个操作,或者更加复杂,在动画过程中用户执行引发另一个动画的操作。这要求动画在任意阶段的回退,或者从一个动画效果无缝切换到另一个效果,并保证 view 最终总能正确呈现 model 的稳定状态。通过把动画效果限制在有限的「罐装」组合,Core Animation 在程序员无需干预的情况下自动实现 transaction [4]。

更重要的是,虽然理论上听起来「罐装」效果对灵活性的牺牲很可怕,实际上 99% 的应用只需要「罐装」效果,因此 Core Animation 作出的牺牲其实是几乎无代价的。当然,不排除由于 Core Animation 的流行,一些原本应该更有创意的动画的地方被用平庸的「罐装」效果来应付。但我认为这是很罕见的情形。而且,动画效果已经从早期的 eye candy  变成提供 UI 行为的 clueness 的普遍要素(比如,当前的 view 变化是因为图像旋转还是图像发生了变化 —— 前者显示为图像旋转而后者则是新旧图片的平移切换)。从少数 fancy 操作扩展到几乎所有 view 的变化。动画的一致性 (consistency) 和 least suprise 变得比创意更为重要。

如果抛开 Core Animation 出现的真实历史 (historical accuracy) 来设想一下一个类似 Core Animation 的库是如何在 Apple 这样的公司出现的。它应该不会是自上而下的需求,不会是为了 OS X 的市场而设计的 feature。相反,像 Apple 这样的公司会对内部产品的界面动画有一套详尽的关注细节的指导原则,最初必然是由程序员用手工 MVC 实现,只需要极少创意和极复杂的 transaction 处理。于是一段时间之后程序员们终于把动画效果「罐装」起来,此后原来的指导原则也几乎不再有提及的必要,因为几行代码就能自然地实现,反而是没有动画效果的操作显得不够自然了。这必然是从真实的痛苦中诞生的技术。

脚注:

  1. 看的是 OS X 版本,所以对 iOS 平台的描述仍然不尽明了。
  2. 比如很多场合人们宣称 GPU 的优势在于大规模并行。这当然会引来一些思维敏捷的初学者的疑问 —— 为什么 Intel 们不把 CPU 做得一样并行化。而 GPU 的威力真正来源是它把要处理的数据结构局限在异常简单而且可以完美对齐的方式,并且配以高速的内存总线。最直接的证据就是无论 OpenGL 还是 OpenCL,在可预见的未来都不会支持指针。
  3. 如上 GPU 对数据结构的妥协。
  4. 底层实现上,Core Animation 通过三套 layer tree 实现自己的 MVC 结构来实现 transaction。这同样是由于把问题局限于「罐装」效果才可能做到。Core Animation 自己的 mini-MVC 让 app 的 MVC 架构脱离了动画 transactional 的负担。