bodymovin deep a little

本文作者:ivweb caorich

data.json 文件格式

以下的内容当设计接口的详细说明时,请移步bodymovin的官方文档。文档的是用JSON Schema编写的,这玩意儿就是一个词汇表,相当于本体的json实现,规范了对象、属性的表达方式(而已)。

言归正传。了解AE导出的data.json数据格式的最好方法就是先制作一个简单得不能再简单的关键帧动画,看看它导出的data.json是什么样的。

我们用AE制作了一个简单的动画,一个宽100,高200的长方形,在400*400的正方形白色底的画布上,从位置(100,200)移动到(300,200)。动画的时间长度为1s, fpr为30(一秒30帧)。现在我们导出data.json文件来看看

 {
    "v": "4.6.7",   // 版本号
    "fr": 30,     // 帧率 Frame Rate,也就是fps,每秒帧数,这里是每秒30帧数
    "ip": 0,      // 开始帧 in point
    "op": 30,     // 结束帧 out point
    "w": 400,    // 画布宽度
    "h": 400,    // 画布高度
    "nm": "testComp",    // 合成的名称。AE中将多个图层的层叠叫做“合成”
    "ddd": 0,     // 未知
    "assets": [],    // 引用资源列表
    "layers": [...]   // 图层的信息,其元素是
}

再看layer属性:

 "layers": [{
    "ddd": 0,   // is 3d layer? boolean
    "ind": 1,    // index
    "ty": 4,    // type {1: solid, 2:image, 3: null, 4: shape, 5: text}
    "nm": "Shape Layer 1",   // name
    "ks": {...},   // transform 用于描述变换规则
    "ao": 0,     // auto orient,布尔值
    "shapes": [{   //图层中的shape列表
        "ty": "gr",   // type: 这里是gr指group
        "it": [{...}],   
        "nm": "Rectangle 1",   //name
        "np": 3,     // number of properties. 属性的数量
        "cix": 2,    // 未知
        "ix": 1,     // 未知
        "mn": "ADBE Vector Group"   //Match Name, 在AE中对应的类型名称
    }],
    "ip": 0,    // in point
    "op": 30,  //out point
    "st": 0,  //start time, start time of this layer
    "bm": 0,   // blend mode
    "sr": 1   // stretch, layer time stretching
}]

layers是一个数组对象,本文的例子只有一个图层,所以layers中只有一个元素。在指明一些基本信息之余,关键看ks属性和shapes属性。ks属性定义了该图层的变换规则。而shapes属性定义了图层中各个形状(shape)。在本文的例子中,只包含一个图层,该图层包含一个形状:“adbe Vector Group”,即我们画的矩形。 先看看ks属性中的内容:

 "ks": { 
    "o": {      // opacity
        "a": 0,   // 布尔值,判断这个属性是否有动画
        "k": 100   // value
    },
    "r": {   // Rotation
        "a": 0,
        "k": 0
    },
    "p": {   // Position
        "a": 1,   // 有动画
        "k": [{   // 如果有动画,那么这个值就是一个‘multiDimensionalKeyframed’
            "i": {   // Bezier curve interpolation in value. 
                "x": 0.833,
                "y": 0.833
            },
            "o": {   // Bezier curve interpolation out value.
                "x": 0.167,
                "y": 0.167
            },
            "n": "0p833_0p833_0p167_0p167",
            "t": 0,  // time
            "s": [100, 200, 0],  // start value
            "e": [300, 200, 0],  // end value
            "to": [31.4798240661621, 0, 0],
            "ti": [-41.3984909057617, 0, 0]
        }, {
            "i": {
                "x": 0.833,
                "y": 0.833
            },
            "o": {
                "x": 0.167,
                "y": 0.167
            },
            "n": "0p833_0p833_0p167_0p167",
            "t": 30,   
            "s": [300, 200, 0],
            "e": [300, 200, 0],
            "to": [2.43751406669617, 0, 0],
            "ti": [-1.8535099029541, 0, 0]
        }, {
            "t": 31
        }]
    },
    "a": {  // Anchor Point 变换的锚点
        "a": 0,
        "k": [0, 0, 0]   // 本例中动画的锚点在中心
    },
    "s": {  // scale
        "a": 0,
        "k": [100, 100, 100]
    }
},

ks属性描述关键帧信息。从上面的例子可以看到,矩形从第0帧的[100, 200, 0]位置,移动到了第30帧的[300, 200, 0]位置。很明显的关键帧表述。关于关键帧的表述第二小节详细讲述。

然后我们来看看shapes属性中的内容。shapes是一个数组,里面放着各种shape。每个shape由相关组件(item)组成,例如本例中,一个圆角矩形的shape由四个部分组成

  • round corner
  • stroke
  • fill
  • transform

round corner定义了外形,stroke定义了边框,fill定义了填充,transform定义了变换。

 "it": [{
    "ty": "rc",   // type = round corner 圆角矩形
    "d": 1,
    "s": {   // size
        "a": 0,
        "k": [100, 150]
    },
    "p": {   // position
        "a": 0,
        "k": [0, 0]
    },
    "r": {   // roundness
        "a": 0,
        "k": 10
    },
    "nm": "Rectangle Path 1",
    "mn": "ADBE Vector Shape - Rect"
}, {
    "ty": "st",   // type: stroke
    "c": {   // color
        "a": 0,
        "k": [1, 1, 1, 1]
    },
    "o": {   // opacity
        "a": 0,
        "k": 100
    },
    "w": {  // width: stroke width
        "a": 0,
        "k": 0
    },
    "lc": 1,   //line cap: {1:butt, 2:round, 3: mint}   
    "lj": 1,   // line join: {1: miter, 2: round, 3: butt}
    "ml": 4,   // miter limit.  
    "nm": "Stroke 1",
    "mn": "ADBE Vector Graphic - Stroke"
}, {
    "ty": "fl",   // fill
    "c": {   // color
        "a": 0,
        "k": [1, 0, 0, 1]
    },
    "o": {   // opacity
        "a": 0,
        "k": 100
    },
    "r": 1,   // 文档中没有r,应该是没用的
    "nm": "Fill 1",
    "mn": "ADBE Vector Graphic - Fill"
}, {
    "ty": "tr",   // transform
    "p": {    // position
        "a": 0,
        "k": [0, 0],
        "ix": 2  // index
    },
    "a": {   // anchor point
        "a": 0,
        "k": [0, 0],
        "ix": 1
    },
    "s": {   // scale
        "a": 0,
        "k": [100, 100],
        "ix": 3
    },
    "r": {   // rotate
        "a": 0,
        "k": 0,
        "ix": 6
    },
    "o": {   // opacity
        "a": 0,
        "k": 100,
        "ix": 7
    },
    "sk": {   // skew
        "a": 0,
        "k": 0,
        "ix": 4
    },
    "sa": {    // skew axis
        "a": 0,
        "k": 0,
        "ix": 5
    },
    "nm": "Transform"
}],

化繁为简

如果你没时间完整读完整读完上面的json文件接口分析。这里给出了一个简单介绍。data.json是对AE文件的完全(虽然很多高级功能不支持)描述。AE文件中将一个合成描述为多个layer,每个layer上有很多shape,每个shape可以由多个shape组成。shape可以自定义变换,整个layer也可以自定义变换。

可以用下图来表示

再用一个简化的json来表示就是:

var comp = {
    layers:[{
        name: 'layer1',
        shapes: [{
            name: 'shape1',
            stroke: {...},
            fill: {...},
            transform: {
                scale: {...},
                translate: {...},
                rotate: {...},
                skew: {...}
            }
        },{
            ...
        }]
    },{
        ...
    }]
}

看到scale\translate\rotate属性,是不是感觉和css的动画就很像了~

bodymovin.js源码分析

bodymovin的源码其实很好看懂。如果略去复杂的矩阵运算和关键帧的运算过程。bodymovin其实就三大块儿:

  • AnimationItem
  • Renderer
  • AnimationElements

AnimationItem是接口的汇总。任何用bodymovin播放的动画都会抽象成一个AnimationItem。渲染动画时,AnimationItem调用renderFrame方法,将渲染过程移交给Renderer,后者根据data.jsonlayers生成不同的动画单元ElementsElements进行渲染工作。基本的构建见下图。

不同的Element的渲染方法,细化下来,都是一些基本的渲染方法。例如,以下是ShapeELement的render方法:

CVShapeElement.prototype.renderShape = function (parentTransform, items, data, isMain) {
    // ...

    if (items[i].ty == 'sh' || items[i].ty == 'el' || items[i].ty == 'rc' || items[i].ty == 'sr') {  // 绘制路径
            this.renderPath(items[i], data[i], groupTransform);
        } else if (items[i].ty == 'fl') {  //渲染fill
            this.renderFill(items[i], data[i], groupTransform);
        } else if (items[i].ty == 'st') {  //渲染stroke
            this.renderStroke(items[i], data[i], groupTransform);
        } else if (items[i].ty == 'gr') {  //渲染子shape
            this.renderShape(groupTransform, items[i].it, data[i].it);
        } else if (items[i].ty == 'tm') {
            //
        }
}

二次开发

那么,基于上面的分析:

bodymovin.js写好了复杂的Element类,等于向我们提供了一个强大的渲染引擎。在接口层AnimationItemRenderer类中,我们可以添加自己的方法来最大程度的发挥bodymovin的作用为己所用。

目前,bodymovin已经为我们提供了方便的接口。

bodymovin.js的减包实践

基本思路

bodymovin同时支持canvas、h5、svg的方式渲染data.json。对于我们日常的动画需求,当遇到需要运用bodymovin来实现的动画效果,势必都是难以用原生css3完成的,所以,让bodymovin来支持h5的渲染没有必要。另外,svg涉及大量的dom操作,其效率比canvas低。

所以,对于减包操作,我首先想到的是将h5和svg的渲染代码砍掉,这样应该能减少不少代码量。

减包后的bodymovin我称作bodymoon,压缩后代码从240K减小到160K。

实践

最近在写一些顶层接口,使得bodymoon使用起来更加方便。

原文链接:http://www.ivweb.io/topic/592864df09439b0640aefbb9

原创声明,本文系作者授权云+社区发表,未经许可,不得转载。

如有侵权,请联系 yunjia_community@tencent.com 删除。

编辑于

我来说两句

0 条评论
登录 后参与评论

相关文章

来自专栏Google Dart

AngularDart Material Design 滑块 顶

可以通过使用鼠标拖动滑块或使用键盘来控制滑块。 在LTR中,向左/向下箭头键将值减1,向上/向右键增加1,向上翻页增加10%(向上舍入),向下翻页减少10%(向...

1072
来自专栏24K纯开源

ChartDirector应用笔记(一)

ChartDirector介绍 ChartDirector是一款小巧精细的商业图表库。其适用的语言范围非常广泛,包括.Net, Java, Asp, VB, ...

2017
来自专栏ytkah

css自动换行如何设置?url太长会撑开页面

  我们更新文章时如果有引用其他文章一般会带一个原文url,但这个链接如果太长的话会把内容的版块撑开,整个排版乱了。那我们能不能设置css自动换行呢?如下图所示...

2595
来自专栏Android群英传

记一次代码中毒急救

932
来自专栏Android小菜鸡

Andorid pcm转码wav

参考文章:https://blog.csdn.net/hesong1120/article/details/79043482

772
来自专栏牛客网

前端实习面经(回馈牛客网)

1413
来自专栏落影的专栏

使用VideoToolbox硬解码H.264

前言 使用VideoToolbox硬编码H.264 在上一篇的硬编码简单介绍了H.264和VideoToolbox以及如何使用VideoToolbox硬编码从...

4385
来自专栏老司机的简书

老司机带你走进Core Animation 之粒子发射、TileLayer与异步绘制

老司机带你走进Core Animation 之粒子发射、TileLayer与异步绘制

812
来自专栏练小习的专栏

常用的命名

命名参考 常用的CSS命名规则: 头:header 内容:content/container 尾:footer 导航:nav 侧栏:sidebar 栏目:co...

1855
来自专栏BinarySec

NETBIOS主机名编码算法

最近在看SMB协议,在自己构造数据包的时候发现了一个问题。 经过查阅资料发现NETBIOS对主机名的编码方式如下: 1.将字符补齐到16字节,不够的用空格补 ...

3508

扫码关注云+社区