Keyframes: 为移动设备提供可缩放、高质量的动画

原文:Keyframes: Delivering scalable, high-quality animations to mobile clients 

表情包是Facebook上人们表达自己的一种方式。当我们做这个功能的时候,我们希望在每一步都能提供一个高质量的体验,包括表情的显示,表情如何进入屏幕,以及如何消失。

15163974_1707735256137694_4835963691591532544_n.gif

我们希望表情图片能执行动画,这点在当前来说是一个挑战。我们希望动画是高质量的,足够小不至于影响性能,同时可以缩放成不同大小。我们探索了现有的的图片和动画形式,但是每种解决方案都和我们的设计需求有不符的地方。

这使得我们开发了Keyframes,一个可以导出和播放After Effects动画的库。自从第一个版本以来,我们学习了更多关于动画的知识,不断改进这个库,把它用到Facebook的其它产品中。今天,我们很高兴把之开源,分享给更多的人,让其它人也能一起创建一个好的产品。

挑战

我们想找到一种满足如下需求,可以用来播放动画的图片格式:

  • 可缩放:表情可以显示为不同的尺寸,根据用户是否移到某个特定表情之上增大或缩小。这意味着要避免静态图片格式比如PNG。

    15164638_1750442168612983_5679334568029585408_n.png

  • 质量:我们希望尽量保持表情每个部分移动的细节-尤其是面部表情,因为这些细节可以让它们看起来更生动。我们还希望在高端设备上动画以高帧率(60 fp)播放并确保表情可以在移动设备上流畅的渲染。

![15163999_336392540070593_8637842306487025664_n.gif](https://upload-images.jcodecraeer.com/upload-images-old/uploads/20161129/1480384329915329.gif "1480384329915329.gif")
  • 文件大小:显示不同大小的表情,高帧率,高分辨率可能会导致很大的文件大小。要把每个文件从磁盘读取到内存是耗时的,而且内存持有这些文件也会占用设备的空间。但是我们并不想压缩图片,因为那样会降低质量。

现有方案

我们开始探索现有的各种解决方法,以理解别人遇到这个问题的时候是怎么做的,看看那个方案能最大程度满足我们的需求。

最初我们看了常见的图片格式比如PNG和GIF,以及压缩更好的WebP。很快就知道如果不大量减少动画和文件大小,这些根本就不能工作,而且我们还必须接受静态图片不能很好缩放的缺点。

想要缩放图片却又想看起来清晰这让我们想到了另一种图片格式SVG,一种矢量图形。和每个描述像素点颜色的静态图形不同的是,SVG描述的是一组绘制命令,这样就能按任意指定大小绘制图片。另一个好处是一些浏览器支持通过SMIL或者CSS让SVG播放动画。但是SMIL只有部分浏览器支持,而使用CSS意味着我们需要找到一种把CSS动画解析然后在原生Android和iOS上实现的方法。

接下来我探索了一些自定义的方法。团队从 bodymovin中得到灵感,bodymovin是一种把Adobe After Effects composition转换成metadata然后使用JavaScript绘制回来的库。虽然这个方法非常有创意,但是它支持了太多我们不需要的功能。我们只需要简单的形状和变换,可能会丢弃文件中的许多metadata。

Keyframes介绍

到了这个阶段,我们知道了我们需要的是一个包含简单动画信息的矢量图形。现在的困难在于如何理解每个After Effects composition中是什么东西以及我们该如何提取需要的数据。

我们先使用一些工具帮助我们把composition model中的所有内容导出到一个JSON文件中。我们开始分析每个文件,从最简单的表情love开始一直到更复杂的,理解动画是如何建立的。

很快我们就知道了重现一个动画的必要概念。每个表情包含了几个composition layers,但是每个layer后面的数据可以归纳为两类-shapes,描述表情的视觉特性,animations,描述每个shape如何随着时间变化。

我们提取出的最基本的数据是一个path对象,由一系列绘制任意尺寸线条的vector命令组成。每个path对应表情中的一个特性。

动画的提取就要复杂点了。动画的一个关键部分是keyframes(关键帧),它表示一个动画过程中起到关键作用的那一帧。比如在Love heart的缩放图像中,在第四帧处就是关键帧,我们让一下子放大了97%。除了这些关键点,还有一些额外数据,内切和外切,描述值是如何从一帧到下一帧的。这些由关键帧和变换组成的动画,可以是shape layer的一部分,描述一个特定的元素如何随着时间变化。也可以自己称为一个layer,被各种shape引用。 

15163988_203579283424383_3986713717889826816_n.jpg

有了这些工具,我们就能使用iOS的Core Animation和Android graphics library中的Paths和Matrices建立一个从JSON文件渲染图像的库。JSON文件只需要最少的必要数据,通常就是数字数组的组合。这个格式允许我们以任意大小准确的回放一个动画,同时还可以很好的控制动画的进度和速度。以Keyframes logo中的一个shape layer为例,描述了shape从19帧到31帧,从起始vector line到最终vector line的变换:

{
  "name": "Animated Line",
  "from_frame": 19,
  "to_frame": 31,
  "key_frames": \[
    {
      "start_frame": 19,
      "data": \[
        "M951.18,654.82", "L960.00,663.63", "L1083.63,540.00", 
        "L960.00,416.37", "L954.18,422.18"
      \]
    },
    {
      "start_frame": 31,
      "data": \[
        "M974.19,545.57", "L974.62,546.00", "L980.62,540.00", 
        "L974.62,534.00", "L974.34,534.28"
      \]
    }
  \],
  "timing_curves": \[
    \[
      \[
        0.5, 0
      \],
      \[
        0.916666666665, 1
      \]
    \]
  \],
  "stroke_color": "#ff1c56c8",
  "stroke_width": 90}

其它应用以及未来计划

自从今年初我们把Keyframes和表情产品放出以来,做了许多改进减轻这个库并提高了图片渲染的性能。一些团队还摸索出了Keyframes的其它用处,比如驱动粒子效果或者利用我们动画中有的纹理控制。随着我们对这个库的使用,认识到哪些设计师想老这样的工具,我们希望能开发出Keyframes库支持的各种After Effects工具。

15163977_298177397242602_5682248149289140224_n.gif

[左] Facebook庆祝世界和平日的Love 粒子效果。 [右] 万圣节糖果风格的Love Reaction

要了解Keyframes库的更多知识或者贡献代码,访问 https://github.com/facebookincubator/Keyframes