用视频软件AE + bodymovin制作网页动画

4,655 阅读10分钟

我们知道,做动画有多种形式,可以用CSS的animation,也可以用Canvas,或者是用JS控制CSS的属性形成动画。我们经常使用CSS做一些比较简单的动画,像过度、加载的动画,对于一些比较复杂的,可能会做成gif,或者是用Canvas,使用Canvas的控制粒度可以很细,同时工作量相对也比较大。做动画还有其它的方式,那就是使用After Effect(AE)/ Flash/Premiere(Pr)/会声会影等视频软件,这种可视化的制作方式相对于直接写代码来说,会更容易简单自然。做动画本身应该使用工具进行制作,但是这种视频软件做出来的动画最后都是生成视频文件,并且通常体积还很大,没有办法直接移植到网页上去。

然而好消息是,现在我们可以使用AE做动画,然后使用bodymovin插件导出成html文件进行播放。AE是Adobe推出的一个很出名的视频后期处理软件,有些电影就是用AE做的,如变形金刚,还有人把AE当成加强版PS使用。也就是说假如我们可以AE做出一些电影级别的效果,然后用html播放,那是一件多么酷炫的事情。

1. 安装bodymovin

bodymovin是一个AE的一个开源的第三方扩展,github地址。上面可以下载这个插件。然后再安装一个ZXPInstaller来安装这个文件,然后重启AE就可以了,当然前提是你要安装一个AE。它支持AE CC版本:

After EffectsCC 2017, CC 2015.3, CC 2015, CC 2014

安装完之后,点击AE的菜单Window -> Extensions -> Bodymovin就会弹出一个窗口:

2. 使用AE制作动画

我相信很多人都没有玩过AE,所以这里我简单地介绍一下。首先新建一个工程(project),然后新建一个合成(composition),选择1080p/29fps,时长为10s,它就会创建一个10s的合成。如下时间轴面板的显示:

这个时间轴将会是频繁操作的地方。点击文字工具,在上方的预览窗口选中一个位置点击创建文字,然后把它拖到窗口外面,因为我们准备做一个文字从外面进来的动画,所以刚开始它是在外面的。把上图右边的蓝色竖线表示的时间线拖到0s的位置,然后在左边的文字图层的Position属性打一个关键帧,如下图所示:

然后把时间线挪到3s的位置,改变文字的Position,把它挪到窗口的中间,这个时候AE会自动在时间线的位置打一个关键帧,如下图所示:

然后按一下空格键进行预览,预览窗口就会播放起了我们刚刚设定的动画:

你会发现,这个过程不是和CSS的keyframe动画一样的么?没错!动画的原理都是一样,通过设定关键帧制作动画。现在来比较一下用AE和用CSS/Canvas做这个动画的区别。

3. 关键帧动画

现在用CSS做这个动画,如下代码所示:

<style>
.text{
    animation: move 3s linear infinite;
}

@keyframes move{
    from{
        transform: translateX(-320px);
    }
    to{
        transform: translateX(100px);
    }
}
</style>
<div class="container">
    <p class="text">Hello, frontend</p>
</div>

我们给animation添加一个动画,这个动画有两个关键帧,分别在0%和100%的位置,需要变化的是transform的属性。这段代码在浏览器运行,就会有刚刚用AE做的动画的效果了。如果用Canvas呢,应该怎么实现呢?

如下代码所示:

<canvas id="text-move" width="600" height="400"></canvas>
<script>
!function(){
    window.requestAnimationFrame(draw);
    var canvas = document.querySelector("#text-move"),
        ctx = canvas.getContext("2d");
    function draw(){
        //计算文字position
        var textPosition = getPosition();
        drawText();
        window.requestAnimationFrame(draw);
    }    
}();

这个是canvas动画的基本框架,先注册requestAnimationFrame的draw函数,使得浏览器在重新绘制屏幕时会先调一下这个函数,理想情况下1s会绘制60幅图片,也就是说1s为60帧/60fps。

上面代码最关键的地方是在于计算文字位置position,同样地,也是要先设定初始位置和终点位置还有动画时间,从而知道移动的速度v,即每1s多少距离,记录一个动画开始时间,然后在每次draw的时候用Date.now()获取当前时间减掉开始时间,就得到时间t,然后用v * t就可以得到位移。这就是用Cavans做动画的基本原理,我们看到,用Canvas需要自己实现一个关键帧系统。

从抽象级别来看的话,AE > CSS >> Canvas,使用AE我只需要拖一拖,然后打上几个关键帧,而使用CSS,我需要把我的操作写成代码,而使用Canvas我需要从0开始一点一点去控制,当然你可以使用一些动画和游戏的引擎提高效率。所以如果有一个可视化界面让你去完成一些复杂的操作,和让你一行一行去写代码的方式选择的话,我想大部分人应该会选择前者。当然这两者的区别不仅仅是操作上的简便性,使用AE借用插件还可以快速地制作出一些复杂的效果。

4. bodymovin小试牛刀

刚刚已经用AE做了一个最简单的动画,现在用bodywin把它导出来。打开bodymovin,选中合层,选择输出路径,如下图所示:

然后点击Render,完成它会输出一个json文件,打开这个导出的文件:

{"v":"4.10.1","fr":29.9700012207031,"ip":0,"op":95.0000038694293,"w":1920,"h":1080,"nm":"Comp 1","ddd":0,"assets":[],"fonts":{"list":[{"origin":0,"fPath":"","fClass":"","fFamily":"Myriad Pro","fWeight":"","fStyle":"Regular","fName":"MyriadPro-Regular","ascent":70.9991455078125}]},"layers":[{"ddd":0,"ind":1,"ty":5,"nm":"hello, frontend","sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":1,"k":[{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"n":"0p833_0p833_0p167_0p167","t":0,"s":[-1017,692,0],"e":[458,692,0],"to":[245.83332824707,0,0],"ti":[-245.83332824707,0,0]},{"t":90.0000036657751}],"ix":2},"a":{"a":0,"k":[0,0,0],"ix":1},"s":{"a":0,"k":[100,100,100],"ix":6}},"ao":0,"t":{"d":{"k":[{"s":{"s":164,"f":"MyriadPro-Regular","t":"hello, frontend","j":0,"tr":0,"lh":196.8,"ls":0,"fc":[0,0.64,1]},"t":0}]},"p":{},"m":{"g":1,"a":{"a":0,"k":[0,0],"ix":2}},"a":[]},"ip":0,"op":300.00001221925,"st":0,"bm":0}]}

这个文件记录了所有动画的过程,如上加粗字体是我们刚刚打的两个关键帧的位置。然后安装一下bodymovin的引擎,可在github上面下载bodymovin.js或者是npm install一下:

npm install bodymovin

然后就可以使用bodymovin了,如下html:

<!DOCType html>
<html>
<head>
    <meta charset="utf-8">
</head>
<body>
    <div id="animation-container" style="width:100%"></div>
    <script src="node_modules/bodymovin/build/player/bodymovin.js"></script>
    <script src="index.js"></script>
</body>
</html>

index.js如下代码所示:

var animation = bodymovin.loadAnimation({
    container: document.getElementById('animation-container'),
    renderer: 'canvas', //svg、html
    loop: true,
    autoplay: true,
    path: 'data.json'
})

调用它的loadAnimation的API,传几个参数,它支持canvas/svg/html三种形式,也就是说它可以用canvas做动画,也可以用svg和html,其中canvas的性能最高,但是canvas有很多效果不支持。data.json的位置通过path告诉它。所有的动画就通过改变path指向的data.json文件区分,而其它的参数不用变。也就是说所有的动画内容和效果都是通过data.json控制的。

现在在浏览器上面运行一下,你会发现报了一个错:

后来发现这个错误是因为文字的原因,如果是用canvas的方式的时候要把文字导成svg的形式,而不是一个纯文本然后通过设置font-family,这个可以在bodymovin里面进行设置,如下图所示:

还可以直接导出一个完整的demo,直接打开html就可以运行,这个比较方便。效果如下:

并且我们发现,它的大小和位移都是相对于容器的,当你把窗口拉小,它也会跟着变小。当使用svg的时候,它是用JS控制svg path标签的transform:

当使用html时,它是控制CSS的transform:

我们一个hello, world的工程已经可以跑起来,bodymovin能支持多复杂的动画呢?

5. AE的摄像机

用AE做动画的时候经常会用到AE的摄像机图层,所谓摄像机就是一个视角,默认情况下这台摄像机是从正前方中间拍过去的,我们可以改变这台摄像机的位置,如把摄像机往前推那么内容就会放大,把摄像机往左右移动,那么看到的内容就会发生倾斜,它有很多仿摄像机的参数可以控制:

摄像机属性都可以通过打关键帧做动画,现在我们加上摄像机做3d的动画。做完后,如果还用canvas的话,它会提示你不能使用canvas,因为它目测不支持WebGL转换,如下图所示:

提示说使用了一个3d camera,尝试使用html renderer,这里要改成html。最后的效果如下:

通过检查,可以看到摄像机也是用transform的matrix控制的。完整的dmo可见这个链接

然后我们再继续做复杂的动画

6. 复杂动画

在所有特效里面,笔者最喜欢的是粒子效果,这种效果也是电影里面经常用到的特效,如冰雪女王的冰雪魔法:

还有文字的粒子效果:

但是这种效果我试了一下没有办法导出来,这种效果本身就比较复杂,渲染起来比较耗时,在html实时播放也不太现实。

还有有时候会报一些奇怪的错误,最常报的一个错误是这个:

bodymovin.js:9249 Uncaught TypeError: this.addTo3dContainer is not a function

可能是使用了一些特定效果,触发了它的bug。

但是不要沮丧,我们还是可以导出一些复杂的效果的,做动画这种关键还是在于idea。

例如可以做一个装饰的小动画,如下所示:

一个完整的demo见这个网址

还可以做一个相册视频,效果如下:

完整的demo见这个网址

如果是做成一个1080p的视频,1.5分钟的mp4格式就算压得比较厉害也要100多MB,但是现在我只要加载一个90kb的json文件和一个240kB的库文件以及10Mb左右的图片就可以了。并且里面的文字和图形还是矢量的。

现在来看一下CPU消耗,可以看到这个相册视频svg动画还是很耗CPU的,但是你开一个视频播放器也同样挺消耗CPU资源的。


不管怎么样,bodymovin提供了另外一种做网页动画的全新方式,摆脱那种纯代码控制的黑暗,甚至你都不用学Canvas和WebGL,也可以做出很酷炫的动画。但是无疑这种方式存在一种弊端,那就是没办法添加参数控制,例如我做一个愤怒的小鸟,我得通过拉弓的方向和力度以及小鸟的重量去计算它的轨迹。但是用AE做的只能是纯动画。所以平时可以两者结合。

bodymovin还支持转成IOS/Android代码,我感觉这个东西发展还在初级阶段,网上也没有很多关于这个的介绍。但是随着这个的认可度提升,发展越来越好,说不定以后能够支持更多的特效,甚至可以提供参数支持。