余额宝年年有余中的 lottie 摄像机

简介: 设计师在用 AE 设计 lottie 动画的时候,在想要实现某个方向上的透视变换的效果的时候,会使用到 AE 中的一个叫做摄像机的东西。摄像机沿着一定的轨迹运动,就能够获得一些类似 3D 的效果。今年的余额宝年年有余活动中,设计师就利用了这个特性,类似于下面这个示意。 不管是 lottie-web 还是 lottie-pixi 都是无法支持这个特性的,也没有在社区中看到有相关的解法。这次年年有余中,我们利用 oasis 引擎,使设计师设计的摄像机能够被正确的渲染出来。

图片.png

背景


设计师在用 AE 设计 lottie 动画的时候,在想要实现某个方向上的透视变换的效果的时候,会使用到 AE 中的一个叫做摄像机的东西。摄像机沿着一定的轨迹运动,就能够获得一些类似 3D 的效果。今年的余额宝年年有余活动中,设计师就利用了这个特性,类似于下面这个示意。 不管是 lottie-web 还是 lottie-pixi 都是无法支持这个特性的,也没有在社区中看到有相关的解法。这次年年有余中,我们利用 oasis 引擎,使设计师设计的摄像机能够被正确的渲染出来。

图片.png

设计师在设计这个效果的时候,是将不同的元素放置在 z 轴方向上,然后通过移动摄像机转换视角,达到了一种不断前进的动画效果。如果把上面的动画旋转一下,从 x 轴正方向往负方向看去,可以看到是下面这个样子。不同的元素摆放在 z 轴中的不同位置。

image.gif

01.gif


而摄像机(可以当成是一个眼睛 👀 ),则随着时间的流逝在空间中做位移,就能在时间序列中以不同的视角“拍”下当前的每一帧,从而形成具有透视效果的动画。


概念


下图中的红色箭头展示了摄像机的一个运动轨迹,沿着红色的轨迹运动,可以得到一个类似于文章最开头的动图的效果。

图片.png

在 AE 中,也可以看到摄像机运动的具体轨迹。

001.gif

摄像机在每一帧都在“拍照”,这个“拍照”,是一个透视投影。透视投影是为了获得接近真实三维物体的视觉效果而在二维的画布平面上绘图。简单来说,就是近大远小

这里放出经典的透视投影的模型图。

图片.png

然而物体看上去的大小,除了与它离眼睛的远近有关,还和物体本身的尺寸有关。视角fieldOfVIew )可以取代上述两者,直接比较物体看上去的大小。上面的模型图中,近裁剪平面(nearClipPlane),远裁剪平面(farClipPlane)和视角会形成一个视椎体。在视椎体内部的物体是会被投影到摄像机里的,也就是会渲染在画布上,而视椎体外的物体则会被裁剪。


为了模拟人眼近大远小这一个特性,可以利用透视投影来完成。有关透视投影的基本数学推导,在网上可以找到很多描述,这里就不提了。具体的细节可以参见维基百科上的描述。


回到 AE 中,在使用 AE 设计的有摄像机的动画中,我们至少需要依靠下面这三种数据,来描述 AE 中摄像机的运动及其“拍”下的的每一帧。


  1. 摄像机本身的位移,控制了当前摄像机在哪,也就是 👀 在哪;
  2. 摄像机朝向的角度(或看向的点),也就是 👀 朝哪里看;在 AE 设计中,可以给摄像机设置旋转角度,也可以给摄像机设置目标点,旋转角度和目标点是互斥的,如果都设置了,目标点会无效,以旋转角度为准。由于 oasis 引擎提供了  transform.lookAt 方法,因此可以推荐设计师直接给摄像机设置目标点,如果设计师设计的是旋转角度,则需要在代码中计算一下目标点。
  3. 视角,控制了可视范围有多大。


如下图所示。

图片.png

实现


在 AE 中,使用 bodymovin 插件,导出的摄像机的 json 描述,与导出其他的类型的元素的描述是一样的,只是包括最基本的位移旋转等属性,而缺少了摄像机本身的一些属性。

// bodymovin 导出的摄像机的 lottie json 描述
{
      "ddd": 0,
      "ind": 78,
      "ty": 13,
      "nm": "摄像机",
      "sr": 1,
      "pe": {
        "a": 0,
        "k": 1041.667,
        "ix": 1
      },
      "ks": {
        "a": {
          "a": 1,
          "k": [
            {
              "i": {
                "x": 0.667,
                "y": 1
              },
              "o": {
                "x": 0.333,
                "y": 0
              },
              "t": 104,
              "s": [
                401.104,
                928,
                4495
              ],
              "e": [
                393.104,
                360,
                4615
              ],
              "to": [
                0,
                0,
                0
              ],
              "ti": [
                0,
                0,
                0
              ]
            },
            {
              "t": 150
            }
          ],
          "ix": 1
        },
        "p": {
          "a": 1,
          "k": [
            {
              "i": {
                "x": 0.667,
                "y": 1
              },
              "o": {
                "x": 0.382,
                "y": 0
              },
              "t": 0,
              "s": [
                400,
                900,
                -970.667
              ],
              "e": [
                400,
                900,
                4000
              ],
              "to": [
                2.698,
                -1.641,
                54.22
              ],
              "ti": [
                0,
                0,
                0
              ]
            },
            {
              "t": 150
            }
          ],
          "ix": 2
        },
        "or": {
          "a": 0,
          "k": [
            0,
            0,
            0
          ],
          "ix": 7
        },
        "rx": {
          "a": 0,
          "k": 0,
          "ix": 8
        },
        "ry": {
          "a": 0,
          "k": 0,
          "ix": 9
        },
        "rz": {
          "a": 0,
          "k": 0,
          "ix": 10
        }
      },
      "ip": 0,
      "op": 450,
      "st": 0,
      "bm": 0
    }

而像下图中的 AE 设置中的一些视角等信息,是缺失的。因此,在没办法获取到这些信息的情况下,我们只好手动将我们这些缺失的信息写入。为了更好的支持 lottie 中的摄像机,需要推动 bodymovin 来修改他们的插件,或者我们给他们提 pr(TODO)。

图片.png

oasis 引擎是一个 ECS(Entity-Component-System) 架构的引擎,因此对于我们业务来说,我们的 Lottie 动画是一个 Entity,摄像机(Camera)也是一个 Entity ,我们需要给摄像机添加一个 Component 来控制相机的位置和目标点。

在具体的实现中,创建了一个 CameraScriptComponent,在 onStart 的时候拿到和设置相机的相关属性,在 onUpdate 的时候去更新相机当前的位置和目标点即可。

import { Script } from 'oasis-engine';
class CameraScript extends Script {
  onStart() {
    // 获取到 lottie json 中的相关属性
    // 并设置相机的相关参数,包括投影透视、fov、关闭裁剪、远近裁剪面等数据
    // ... 
  }
  onUpdate(deltaTime: number) {
    // 设置每一帧相机的位置和目标点
    // ... 
  }
}


上面的 json 中,"p"节点下的代表的是相机的位置描述,与其他元素的位置描述一样,它是用两条贝塞尔曲线来(位置曲线和速度曲线)描述的,通过这些曲线的起点终点和控制点获取到每一帧的摄像机位置之后,通过调用:

camera.transform.setPosition(x, y, z);

来更新相机的位置。

"a"节点下的代表的是相机的目标点描述,与"p"节点下位置描述一样,可以计算获取到每一帧的摄像机的目标点的坐标,然后调用

camera.transform.lookAt(new Vector3(x, y, z));

便可以更新相机的目标点。

需要注意的是,lottie json 中的坐标,与 oasis 中的坐标是不一样的,因此在设置的时候,需要转换一下。lottie json 中的坐标是的原点 (0, 0) 是画布的左上角,而 oasis 中的坐标的原点 (0, 0) 是画布的中心。

// lottie 的坐标 转换成 oasis 里面的坐标
function convertCoords(vector3: Vector3, w = 750, h = 1624, pixelsPerUnit = 128) {
  const result = vector3.clone();
  result.x = (result.x - w / 2) / pixelsPerUnit;
  result.y = -(result.y - h / 2) / pixelsPerUnit;
  result.z /= pixelsPerUnit;
  return result;
}

为了能让 z 方向上所有的元素,都能出现在摄像机的视野范围内,需要给摄像机设置远近裁剪面。设计师在 AE 中设计摄像机的时候,已经把摄像机和每一层的元素放置好了,因此,不需要给远近裁剪面设定一个唯一的值,只需满足一定的条件就行了,确保裁剪面的范围能够包括下所有的元素。也就是

0 < nearClipPlane < abs(距离相机最近的图层 - 相机的位置)
farClipPlane > abs(距离相机最远图层 - 相机的位置)

有时候,一个 lottie 是可以用不同的速度播放的,在年年有余的小游戏中,也允许通过同时控制 lottie 的播放速度与相机的运动速度来展示出慢速或快速的动画。由于我们是给一个 Camera增加了一个 Component来更新相机的位置,因此只需要在 Script中的 onUpdate里面,获取到当前的 LottieAnimation的速度,再同步地算出当前相机的位置,更新给相机即可。


需要注意的一点是,摄像机“拍照”的近大远小的特性,会造成在远处的图片异常的大。在将 lottie 中的图片合并成雪碧图的时候,很容易就超过了 2048x2048 的大小,此时,需要将雪碧图拆分成多个(或者使用 base64的方式),否则会造成闪退。


参考链接

oasis-engine: https://github.com/oasis-engine/engine

相关文章
|
2月前
|
定位技术 开发工具 Python
代码让微信开屏地球转起来
这段内容介绍了如何使用Python制作一个动态旋转的地球图像,类似于微信启动画面中的地球。文章详细描述了所需准备的三个素材(地球表面、云图和微信地球的抠图),并解释了通过调整云层和地面的旋转速度来实现自然的相对运动效果的基本原理。文中还提供了一个GIF压缩工具的链接,以及部分代码示例。此外,文章最后提到了一些Python学习资源,包括学习路线、开发工具、视频教程、实战案例、练习题和面试资料。
44 0
|
NoSQL 前端开发 JavaScript
大地影院订票app
大地影院订票app
196 0
|
JavaScript 小程序 前端开发
涂鸦小程序——为自己的人生画上一笔
涂鸦小程序——为自己的人生画上一笔
251 0
涂鸦小程序——为自己的人生画上一笔
|
Android开发 编解码 UED
当手机淘宝遇见折叠屏,让购物更随心
伴随手淘技术团队对华为折叠屏适配工作的展开。半年前还只是概念方案的分屏设计方案已正式实现。华为折叠屏上的淘宝已全面支持分屏多任务,以后商品比价、边逛边聊更加轻松便捷,为大家带来不一样的购物体验。
2396 0
当手机淘宝遇见折叠屏,让购物更随心
|
存储 监控 物联网
从自动支付到自主支付,“物体”会支付吗?
物联网为创新带来了巨大的兴奋和机遇,这说明了它对银行和支付行业的影响,并引起了越来越多的关注。
白天我在阿里写代码,入夜还有我的麦克风!
昨天下班,IT小哥过来神神秘秘地说,走吗?去酒吧听我唱歌。 说着往我手里塞了个手牌,自己往格子衬衫外面套个黑色皮衣,头也不回地走入夜色。 五新?原来他就是传说中那个白天写代码,入夜唱摇滚的“阿里汪峰”五新? ——啊,等等我啊喂! 推门进入前,我抬头看了看,霓虹招牌在夜色里闪烁: Happy Honey Badger (快乐的平头哥) 欸,这造型有点眼熟——欢迎来到阿里十派年度歌王总决选。
1864 0
|
Web App开发
魅族手机里的便签怎么导出转移到新的手机上?
魅族手机时尚简约,清新文艺,受到不少年轻人的青睐。 其手机便签可以设置主题,既文艺又别致。同时,如果想要把旧的魅族手机里的便签内容导出转移到新的魅族手机上,只需要在旧的魅族手机中开启云同步功能,将便签内容同步到云端,然后在新的魅族手机上登录自己的魅族账号,能够把云端的便签内容同步到新手机上。
7695 0