Lottie 详解

简介

Lottie 是 Airbnb 开源的一个面向 iOS、Android、React Native 的动画库,能分析 Adobe After Effects 导出的动画,并且能让原生 App 像使用静态素材一样使用这些动画,完美实现动画效果。

现在使用各平台的 native 代码实现一套复杂的动画是一件很困难并且耗时的事,我们需要为不同尺寸的屏幕加载不同的素材资源,还需要写大量难维护的代码,而 Lottie 可以做到同一个动画文件在不同平台上实现相同的效果,极大减少开发时间,实现不同的动画,只需要设置不同的动画文件即可,极大减少开发和维护成本。

官方效果图:

官方效果图

使用

Lottie 支持多平台,使用同一个 JSON 动画文件,可在不同平台实现相同的效果。其中,Android 通过 Airbnb 的开源项目 lottie-android 实现,最低支持 API 16。

引入依赖

implementation 'com.airbnb.android:lottie:2.5.4'

添加 Adobe After Effects 导出的动画文件

Lottie 默认读取 Assets 中的文件,我们需要把动画文件 react.json 保存在 app/src/main/assets 文件里。在 官网 可以免费下载动画文件。

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
// react.json
{
"v": "4.6.0",
"fr": 29.9700012207031,
"ip": 0,
"op": 141.000005743048,
"w": 800,
"h": 800,
"ddd": 0,
"assets": [ ],
"layers": [
{
"ddd": 0,
"ind": 0,
"ty": 4,
"nm": "center_circle",
"ks": {...},
"ao": 0,
"shapes": [...],
"ip": 0,
"op": 900.000036657751,
"st": 0,
"bm": 0,
"sr": 1
},
{...},
{...},
{...}
]
}

使用Lottie

在布局文件中直接添加 Lottie 的 LottieAnimationView 控件,即可在界面显示 React logo 动画效果

1
2
3
4
5
6
7
<com.airbnb.lottie.LottieAnimationView
android:id="@+id/animation_view"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
app:lottie_fileName="react.json"
app:lottie_loop="true"
app:lottie_autoPlay="true" />

属性说明:

  • app:lottie_fileName: assets 文件夹下对应的动画文件的文件名
  • app:lottie_loop: 是否循环播放动画
  • app:lottie_autoPlay: 是否自动播放动画
  • app:lottie_cacheStrategy: 设置缓存策略。默认为none,可选值有strong和weak
  • app:lottie_colorFilter: 设置背景颜色
  • app:lottie_progress: 设置动画播放进度
  • app:lottie_imageAssetsFolder: 动画所需图片资源在assets中的绝对路径。如果没有图片资源,可以省略

代码使用:

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
LottieAnimationView animationView = (LottieAnimationView) findViewById(R.id.animation_view);

animationView.playAnimation(); //播放动画

animationView.setAnimation("data1.json", CacheStrategy.Strong); // 设置动画文件以及缓存策略

PorterDuffColorFilter colorFilter = new PorterDuffColorFilter(ContextCompat.getColor(this,android.R.color.holo_blue_light), PorterDuff.Mode.ADD);
animationView.addColorFilter(colorFilter); // 设置背景颜色

animationView.loop(true); // 循环播放

animationView.setProgress(0.5f); // 设置动画播放进度。需要注意值是float型。

// 监听动画的状态
animationView.addAnimatorListener(new Animator.AnimatorListener() {
@Override
public void onAnimationStart(Animator animation) {
// 开始
}

@Override
public void onAnimationEnd(Animator animation) {
// 结束
}

@Override
public void onAnimationCancel(Animator animation) {
// 取消
}

@Override
public void onAnimationRepeat(Animator animation) {
// 重复
}
});

// 动态监听动画演示过程,可以动态获取动画进度
animationView.addAnimatorUpdateListener(new AnimatorUpdateListener() {
@Override
public void onAnimationUpdate(ValueAnimator animation) {

}
});

animationView.pauseAnimation(); // 暂停动画

animationView.isAnimating() // 是否正在播放动画

animationView.resumeAnimation() // 恢复动画

animationView.cancelAnimation() // 取消动画

使用小技巧

加载SDCard动画文件

1
2
3
4
5
6
7
8
9
10
StringBuilder stringBuilder = new StringBuilder();
BufferedReader bufferedReader = new BufferedReader(new FileReader(new File(JSON_PATH + "react.json")));
String content = null;
while ((content = bufferedReader.readLine()) != null){
stringBuilder.append(content);
}
JSONObject jsonObject = new JSONObject(stringBuilder.toString());
animationView.setAnimation(jsonObject);
animationView.loop(true);
animationView.playAnimation();

加载SDCard图片

1
2
3
4
5
6
7
8
9
10
11
12
animationView.setImageAssetDelegate(new ImageAssetDelegate() {
@Override
public Bitmap fetchBitmap(LottieImageAsset asset) {
try {
FileInputStream fileInputStream = new FileInputStream(IMAGE_PATH + asset.getFileName());
return BitmapFactory.decodeStream(fileInputStream); ///把流转化为Bitmap图片
} catch (Exception e) {
e.printStackTrace();
}
return null;
}
});

加载SDCard字体

1
2
3
4
5
6
animationView.setFontAssetDelegate(new FontAssetDelegate(){
public Typeface fetchFont(String fontFamily) {
Typeface customFont = Typeface.createFromFile(FONT_PATH + fontFamily);
return customFont;
}
});

缓存动画

1
2
3
4
5
/**
*Lottie内部有两个缓存map(强引用缓存,弱引用缓存),在动画文件加载完成后会根据设置的缓存策略缓存动画,方便下次使用。
*/
animationView.setAnimation(animation, LottieAnimationView.CacheStrategy.Strong); //强缓存
animationView.setAnimation(animation, LottieAnimationView.CacheStrategy.Weak); //弱缓存

Lottie实现原理

Lottie 的使用的资源是需要先通过 bodymovin( bodymovin 插件本身是用于网页上呈现各种AE效果的一个开源库) 将 Adobe After Effects (AE) 生成的aep动画工程文件转换为通用的json格式描述文件。Lottie则负责解析动画的数据,计算每个动画在某个时间点的状态,准确地绘制到屏幕上。

Lottie 对外通过控件 LottieAnimationView 暴露接口,控制动画。
LottieAnimationView 继承自 ImageView,通过当前时间绘制 canvas 显示到界面上。这里有两个关键类:LottieComposition 负责解析json描述文件,把json内容转成Java数据对象;LottieDrawable负责绘制,把LottieComposition转成的数据对象绘制成drawable显示到View上。顺序如下:

顺序

探究更多原理解析内容请在 Lottie–让动画如此简单 查看。

性能

内容来自 腾讯音乐技术团队 。

官方说明

  • 如果没有 mask 和 mattes,那么性能和内存非常好,没有 bitmap 创建,大部分操作都是简单的 cavas 绘制。
  • 如果存在 mattes,将会创建2~3个 bitmap。bitmap 在动画加载到 window 时被创建,被 window 删除时回收。所以不宜在 RecyclerView 中使用包涵 mattes 或者 mask 的动画,否则会引起 bitmap 抖动。除了内存抖动,mattes和mask中必要的 bitmap.eraseColor() 和 canvas.drawBitmap() 也会降低动画性能。对于简单的动画,在实际使用时性能不太明显。
  • 如果在列表中使用动画,推荐使用缓存 LottieAnimationView.setAnimation(String, CacheStrategy)

属性动画和Lottie动画对比

以下性能对比是以K歌内单个礼物动画效果

- 属性动画 lottie使用硬件加速 lottie未使用硬件加速
帧率
内存
cpu

总结

Lottie使用简单,易于上手,非常值得尝试。

优势

  • 开发效率高—代码实现简单,更换动画方便,易于调试和维护。
  • 数据源多样性—可从assets,sdcard,网络加载动画资源,能做到不发版本,动态更新
  • 跨平台—设计稿导出一份动画描述文件,android,ios,react native通用

劣势

  • 性能不够好—某些动画特效,内存和性能不够好;
  • 相对于属性动画,在展示大动画时,帧率较低

参考文章

评论

Your browser is out-of-date!

Update your browser to view this website correctly. Update my browser now

×

keyboard_arrow_up 回到顶端