前端动画解决方案Lottie(web篇)

guide.gif

前些天阿里蚂蚁设计开放出了一款动画设计平台——犸良,惊讶了一阵子之后发现它的底层实现借助了airbnb的Lottie库,再次惊讶之后感叹其伟大。

概念

Lottie是一个面向Android、iOS、Web和Windows的库,它解析导出为带有bodymovin的json的AE(Adobe After Effects)动画,并在移动和Web上以本地方式呈现这些动画。

官方社区至今(2019.07.18)统计:下载动画量达1304264次,可见其广泛被大家所接受。

个人觉得Lottie的最大优势就是让设计师直接进行动画的开发,让动效设计和实现都交给专业的设计团队,而不是按传统形式谆谆引导前端开发或直接使用难以维护且性能较低的gif/mp4。解耦动效业务,让开发顺心、设计安心。。。

guide2.gif

安装和使用(web)

首先是根据需求引用lottie库,官方给出的下载地址:https://cdnjs.com/libraries/bodymovin

如图所示,官网地址给出了v5.5.6的所有版本:
lottieLibrary.png
其中:

  • _light:轻量级,按官网说法,以此拓展名的库将播放导出为SVG的动画。
  • _canvas:以此拓展名的库用canvas动画形式展示。
  • _svg:以此拓展名的库用svg动画形式展示。
  • _html:以此拓展名的库用canvas动画形式展示。

使用

根据需求找到Lottie库后,就是该如何使用了

准备容器和动画库

首先准备渲染容器和引用Lottie库,如:

1
2
3
<div id="app"><!-- 动画容器 --></div>

<script src="https://cdnjs.cloudflare.com/ajax/libs/bodymovin/5.5.6/lottie.min.js"></script>

并将AE导出的动画数据库(如lottie-1.json)保存在项目目录中,如assets/lottie-1.json

flow.png

配置参数和渲染

如:

1
2
3
4
5
6
7
8
9
10
<script>
var animation = bodymovin.loadAnimation({
container: document.getElementById('app'), // 容器,必须填
path: 'assets/lottie-1.json', // 动画数据文件,必须填
renderer: 'canvas', // 渲染形式,必须填
loop: true, // 一直循环?,选填
autoplay: true, // 自动播放?,选填
name: "lottie-1", // 命名,选填
})
</script>

这样配置后的页面打开将循环播放一个动画。
p-1.png

这样就是最简单的使用实现了。

配置参数和方法

配置

Lottie的配置很简单,主要配置参数如下:

参数 说明 数据格式 默认值
container 渲染动画的容器元素 DOM Element
path 动画数据json文件的路径(和animationData参数二选一) string
animationData 动画数据(和path参数二选一) object
renderer 渲染方式,'svg'/'canvas'/'html'(轻量版仅svg渲染) string
loop (可选)动画是否循环,为数字时即控制循环次数 boolean/number false
autoplay (可选)动画是否自动播放 boolean true
name (可选)自定义动画名称 string

方法

配置完动画后,我们可以拿到动画实例并通过实例方法进行控制:

  • play():播放动画,从当前停止帧开始
  • stop():终止动画,回到第0帧
  • pause():暂停动画,停止在当前帧
  • setLocationHref(href)href作为location.href,当你在Safari中遇到不带符号的掩码问题时,它非常有用。
  • setSpeed(speed):设置动画速度(1是正常速度)。speed参数为速度,number。
  • goToAndStop(value, isFrame):动画跨越到某进度并停止。value为进度数值,number;isFrame定义第一个参数(value)是基于时间的值还是基于帧的(默认为false)。
  • setDirection(direction):设置动画播放顺序(顺序播放/倒叙播放)。direction参数表明顺序,1为顺序,-1为倒叙。
  • playSegments(segments, forceFlag):播放动画片段。segments参数可以包含两个将用作动画第一帧和最后一帧的数值,或者可以包含一系列数组,每个数组都有2个数值,array。forceFlag表示是否立即强制播放该片段,如果设置为false,它将等待当前段完成。如果为true,它将立即播放此片段,boolean。如animation.playSegments([10,20], false); // 播放完之前的片段,播放10-20帧animation.playSegments([[0,5],[10,18]], true); // 直接播放0-5帧和10-18帧
  • setSubframe(useSubFrames)useSubFrames参数如果为false,则将尊重原始的AE fps。如果为true,它将使用中间值在每个RequestAnimationFrame上更新。默认值为true, boolean。
  • destory():删除该动画,移除相应的元素标签等。在unmount的时候,需要调用该方法
  • getDuration(inFrames),获取动画持续时间。inFrames参数为true,则返回以帧为单位的持续时间;如果为false,则返回以秒为单位的持续时间。

事件

在使用中可能也需要监听一些事件:

  • complete: 播放完成(循环播放下不会触发)
  • loopComplete: 当前循环下播放(循环播放/非循环播放)结束时触发
  • enterFrame: 每进入一帧就会触发,播放时每一帧都会触发一次,stop方法也会触发
  • segmentStart: 播放指定片段时触发,playSegments、resetSegments等方法刚开始播放指定片段时会发出,如果playSegments播放多个片段,多个片段最开始都会触发。
  • data_ready: 动画数据json文件加载完毕触发
  • data_fail:动画数据json文件加载失败触发
  • loaded_images:当所有图片加载成功/失败时触发
  • DOMLoaded: 动画相关的dom已经被添加到html后触发
  • destroy: 将在动画删除时触发

使用如:

1
2
3
4
// 上例的animation实例
animation.addEventListener('data_ready', function () {
console.log('animation data file has loaded');
});

更多配置可见npm lottie-web

打包及框架中使用

原生js+webpack

目录:

1
2
3
4
5
6
7
8
│  index.html

├─assets
│ lottie-2.json // 动画数据json文件

└─js
└─index
enter.js // 入口

其中index.html:

1
2
3
4
5
6
7
8
9
10
11
<!-- index.html -->
...
<body>
<div id="app"><!-- animation container --></div>
<div>
<button id="play">play</button>
<button id="pause">pause</button>
<button id="stop">stop</button>
</div>
</body>
...

准备了一个动画容器和三个控制按钮。

安装依赖npm lottie-web

1
npm i -D lottie-web

引用并配置:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
// enter.js
import lottie from 'lottie-web'
import * as animationData from '../../assets/lottie-2.json'

// 配置
let animation = lottie.loadAnimation({
container: document.getElementById('app'), // 容器,必须填
animationData, // 动画数据
renderer: 'svg', // svg渲染动画
loop: true, // 一直循环
autoplay: false, // 不自动播放
name: "lottie-2" // 命名,选填
});

animation.addEventListener('loaded_images', () => {
console.log('images ready');
})

// set events
let $play = document.getElementById('play'),
$stop = document.getElementById('stop'),
$pause = document.getElementById('pause');

$play.addEventListener('click', () => animation.play(), false);
$stop.addEventListener('click', () => animation.stop(), false);
$pause.addEventListener('click', () => animation.pause(), false);

这样使用便已完成,可通过按钮控制动画。

ReactJs

目录:

1
2
3
4
5
│  App.jsx
│ index.js

└─assets
lottie-2.json

安装依赖(npm react-lottie

1
npm i -D react-lottie

该库将给出一个React组件,用于渲染动画,该组件props:

  • options:配置参数
  • width: 容器宽度,number
  • height:容器高度,number
  • isStopped:是否停止状态,boolean
  • isPaused:是否暂停状态,boolean
  • eventListeners:事件监听,array

使用及配置如下

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
// App.jsx
import React, {Component} from 'react';
import Lottie from 'react-lottie';
import * as animationData from './assets/lottie-2.json'

class App extends Component {
animation = null;

state = {
isStopped: false,
isPaused: false
}

constructor () {
super();
this.handlePlay = this.handlePlay.bind(this);
this.handlePause = this.handlePause.bind(this);
this.handleStop = this.handleStop.bind(this);
}

defaultOptions = {
animationData: animationData.default, // 动画数据
renderer: 'svg', // svg渲染动画
loop: true, // 一直循环
autoplay: false, // 不自动播放
name: "lottie-2" // 命名,选填
}


// handle play
handlePlay () {
this.setState({ isStopped: false, isPaused: false });
}

// handle pause
handlePause () {
this.setState({ isPaused: !this.state.isPaused });
}

// handle stop
handleStop () {
this.setState({ isStopped: true });
}

render() {
return (
<section>
<Lottie
options={this.defaultOptions}
width={700}
height={400}
isStopped={this.state.isStopped}
isPaused={this.state.isPaused}
/>
<div>
<button onClick={this.handlePlay}>play</button>
<button onClick={this.handlePause}>pause</button>
<button onClick={this.handleStop}>stop</button>
</div>
</section>);
}
}

export default App;

注:react-lottie底层实现也是使用了lottie-web这个库,只是将其配置和渲染进行了封装,便于开发使用。可访问github react-lottie通过源码查看其实现原理。

VueJS

目录:

1
2
3
4
5
6
7
8
│  index.html
│ main.js // 入口

├─assets
│ lottie-2.json

└─views
App.vue

安装依赖(npm vue-lottie

1
npm i -D vue-lottie

该库将给出一个Vue组件,用于渲染动画,该组件props:

  • options:配置参数
  • width: 容器宽度,number
  • height:容器高度,number
  • animCreated:注册事件,用于处理动画实例(源码:this.$emit('animCreated', this.anim)

使用及配置如下

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
<!-- App.vue -->
<template>
<section>
<lottie
:options="defaultOptions"
width="700"
height="400"
@animCreated="handleAnimation"
/>
<div>
<button @click="handlePlay">play</button>
<button @click="handlePause">pause</button>
<button @click="handleStop">stop</button>
</div>
</section>
</template>

<script>
import Lottie from 'vue-lottie';
import * as animationData from '../assets/lottie-2.json'

export default {
name: "App",
data () {
return {
animation: null,
defaultOptions: {
animationData: animationData.default, // 动画数据
renderer: 'svg', // svg渲染动画
loop: true, // 一直循环
autoplay: false, // 不自动播放
name: "lottie-2" // 命名,选填
}
}
},
components: {
'lottie': Lottie
},
methods: {
// set instance
handleAnimation (animation) {
this.animation = animation;
},
// handle play
handlePlay () {
this.animation.play();
},
// handle stop
handleStop () {
this.animation.stop();
},
// handle pause
handlePause () {
this.animation.pause();
}
}
}
</script>

注:vue-lottie底层实现也是使用了lottie-web这个库,只是将其配置和渲染进行了封装,便于开发使用。可访问github vue-lottie通过源码查看其实现原理。

Angular

不细说,运用库为npm ng-lottie,底层仍然是lottie-web。

注意事项(设计狮)

下面这些效果,lottie暂不支持:

  • 描边动效目前不支持,(会导致lottie性能问题,所以后来lottie去掉了对该属性的支持)。
  • merge Path(合并路径)Android只支持4.4以上,iOS都不支持.

遵循下面的方案,会使json文件减小:

  • 尽量减少图层个数。每个图层都会导出成相应的json数据,图层减少能从很大程度上减小json大小。
  • 尽可能所有的图层都是在AE里面画出来的,而不是从其他软件引入的。如果是其他软件引入的,很可能导致描述这个图形的json部分变得很大。

并且,Lottie支持的动画效果如下:
table.jpg

lottie-web动画实现原理

先占个坑~

lottie.json

首先来看lottie.json中的关键字段:

  • ip:起始帧
  • op:结束帧
  • w:宽
  • h:高
  • v:版本
  • fr:频率/帧率
  • ddd
  • nm
  • tiny
  • fonts:字体
    • list
      • fontConfig
  • asset:资源
    • assetConfig
      • id
      • w:宽
      • h:高
      • u:路径
      • p:名称
      • e
  • layers:图层
    • layerConfig
      • st:开始帧
      • ip:起始帧
      • op:结束帧
      • nm:名字
      • ind:图层id
      • ty:图层类型
      • ks:动画
      • shapes:图形

相关链接