飞书中 Lottie 动画的应用
背景
飞书项目中 loading 常用的动画方案是 Gif 动画。
Gif 动画存在一定的问题,Gif 文件一般较大,且呈现的大小是固定的,无法缩放以匹配大屏幕和高密度屏幕,容易有锯齿,不能控制动画。
其他常用的动画方案:
Png 序列帧:合成的雪碧图文件大,且在不同屏幕分辨率下可能会失真
SVG 动画: 实现成本高,容易出现动画还原度低的情况
目前项目中需要一种更加简单、高效、性能好、还原度高的动画方案,设计同学正在推动 AE + bodymovin 导出动画配置的方案,经过调研发现 Lottie 动画是一种可行性较高的方案。
Lottie 简介
Lottie[1] 是 airbnb 开源的可应用于 Android[2] , iOS[3] , Web[4] , React Native[5] 和 Windows[6] 动画库, 本质上是一套跨平台的动画解决方案。它提供了一套完整的从 AE 到各个终端的工具流,通过 AE 的 Bodymovin 插件将设计师做的动画导出成一套定义好的 json 文件,之后再通过 Lottie 各端的库就可以实现动画效果,动画还原度 100%, Lottie-web Example[7] 。
使用方法
lottie-web[8] 支持特性最多( http:// airbnb.io/lottie/# /supported-features ),可以实现较为复杂的动画,控制动画的播放,监听动画各个阶段的事件( https:// github.com/airbnb/lotti e-web )。
lottie-web[9] 的使用方法, 有三种渲染方式 svg, canvas, html, 一般常用 svg, canvas
lottie.loadAnimation({
container: element, // the dom element that will contain the animation
renderer: 'svg',
loop: true,
autoplay: true,
path: 'data.json' // the path to the animation json
react-lottie[10] 的使用方法(将 lottie-web[11] 封装成 React 组件)
import React from 'react'
import Lottie from 'react-lottie';
import * as animationData from './pinjump.json'
export default class LottieControl extends React.Component {
constructor(props) {
super(props);
this.state = {isStopped: false, isPaused: false};
render() {
const buttonStyle = {
display: 'block',
margin: '10px auto'
const defaultOptions = {
loop: true,
autoplay: true,
animationData: animationData,
rendererSettings: {
preserveAspectRatio: 'xMidYMid slice'
return <div>
<Lottie options={defaultOptions}
height={400}
width={400}
isStopped={this.state.isStopped}
isPaused={this.state.isPaused}/>
<button style={buttonStyle} onClick={() => this.setState({isStopped: true})}>stop</button>
<button style={buttonStyle} onClick={() => this.setState({isStopped: false})}>play</button>
<button style={buttonStyle} onClick={() => this.setState({isPaused: !this.state.isPaused})}>pause</button>
控制动画播放的方法:
名称 | 描述 |
---|
监听事件:
名称 | 描述 |
---|
Lottie 动画性能测试
Lottie 局部加载动画, Gif 动画 与 Lottie 动画比较
Gif 动画性能
1,加载编译库文件的耗时(阻塞启动)
平均:25ms
2,lottie-web 文件本身的内存占用
shadow size: 136B Retained Size: 1209553B
3,执行时的耗时(体现到耗时上,CPU 采样会很不精准,阻塞业务逻辑,如启动 chat)
平均: 15.4ms
Lottie 动画性能
由上图可知,查看了 Gif 动画、Lottie 动画方案的 FPS、CPU 占用率、GPU 占用、Scripting、Rendering、Painting、内存的使用情况。
方案 | 大小 | FPS | CPU 占用率 | GPU 占用 | 内存 |
---|
通过以上数据分析,可知 Lottie 动画的配置文件较小,帧率较高,GPU 占用低,内存与 Gif 动画相差不大,性能较好。
Lottie 动画方案简单、高效、性能好,可以替代传统的 GIF 和帧动画,灵活利用好提供的属性和方法可以控制动画的播放。
注意事项
及时卸载 Lottie 动画组件
在不需要 Lottie 动画时,需要及时卸载 Lottie 动画组件
飞书出现过偶现 CPU 升高的异常案例,经过排查定位到是 Lottie 动画没有卸载引起的 CPU 升高。页面中已经没有动画,但 Lottie 一直在调用 requestAnimationFrame,导致在没有任何操作的情况下,CPU 占用升高至 2%-5% 左右,一般情况在没有任何操作的情况下,cpu 占用 0.1% ~0.2%。
Lottie 动画调用 react-lottie 组件,组件在 componentWillUnmount 时,会销毁该动画实例。
飞书中 CPU 升高的时候,发现 Lottie 动画中有动画实例尚未销毁,导致会不停的调用 requestAnimationFrame,导致异常的动画是局部加载动画。飞书中用到局部加载的动画的模块有主端(切换会话、联系人页面 、新添加的联系人、机器人、外部联系人、onCall、升级提示弹窗、添加联系搜索、发送云盘文件弹窗、Pin 列表、Docs Webview 加载动画、切换租户)、日历、应用中心等。
经过定位发现,应用中心里用到了局部加载动画, 在不需要动画的时候,没有卸载组件,只是通过 CSS 来隐藏了组件,导致没有销毁 Lottie 动画实例,requestAnimationFrame 会一直执行,代码如下:
// AppHome.js
<div className={!isLoaded ? 'app-home-loadingImg' : 'display-none'}><PartialLoading /></div>
不需要 Lottie 动画的时候,卸载 Lottie 动画组件。
// AppHome.js
!!isLoading && (
<div className='app-home-loadingImg'>
<PartialLoading />