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 条评论
登录 后参与评论

相关文章

来自专栏JackieZheng

可视化工具solo show-----Processing Prefuse show

  继上篇《可视化工具solo show》罗列出一些主要基于Java开发的软件、插件之后,又仔细揣摩了下哪些可以为我所用。   一番端详之后,准备挑出其中Pro...

1696
来自专栏前端儿

表达式求值(1)

Dr.Kong设计的机器人卡多掌握了加减法运算以后,最近又学会了一些简单的函数求值,比如,它知道函数min(20,23)的值是20 ,add(10,98) 的值...

712
来自专栏24K纯开源

ChartDirector应用笔记(一)

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

1897
来自专栏Python攻城狮

Python数据科学(七)- 资料清理(Ⅱ)1.资料转换2.处理时间格式资料3.重塑资料4.学习正则表达式5.实例处理

注意:这里的时间转换后的格式可以根据需要设定,eg:dt.strftime('%Y/%m/%d')

943
来自专栏数据小魔方

think-cell chart系列17——图表异常值的切割表达

今天跟大家分享的是think-cell chart系列17——图表异常值的切割表达。 经常做图表的小伙伴儿恐怕都遇到过这种情况——一些数据中可能会存在异常值,导...

3376
来自专栏腾讯移动品质中心TMQ的专栏

VR中的动画就是这么玩哒

导读 大家是不是觉得VR中的动画特别神奇,其实它是基于Unity中的动画系统Mecanim实现的,Unity在5.0之后,Mecanim动画控制越来越强大好用...

2126
来自专栏青玉伏案

iOS可视化动态绘制连通图(Swift版)

上篇博客《iOS可视化动态绘制八种排序过程》可视化了一下一些排序的过程,本篇博客就来聊聊图的东西。在之前的博客中详细的讲过图的相关内容,比如《图的物理存储结构与...

2097
来自专栏程序猿的那些趣事

web前端学习:HTML5十个新特性

                   :刻度尺/度量衡,描述数据所处的阶段,红色(危险)=>黄色(警告)=>绿色(优秀)

621
来自专栏Android群英传

Android Vector曲折的兼容之路

993
来自专栏BestSDK

Excel必备神器,9个自动批处理,巨量表格工作克星!

01 批量求和 对于某个区域的汇总求和,一个个写公式再拖动填充并不是最简便的做法,批量求和,得看"Alt"+"="来秀一波! ? 02 批量添加单位 还在困惑怎...

29511

扫码关注云+社区