简介
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);
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); } 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
|
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通用
劣势
- 性能不够好—某些动画特效,内存和性能不够好;
- 相对于属性动画,在展示大动画时,帧率较低
参考文章