从json文件到炫酷动画-Lottie实现思路和源码分析

Lottie是最近Airbnb开源的动画项目,支持Android、iOS、ReactNaitve三个平台,相关背景介绍可以参考之前的文章Airbnb开源炫酷动画库Lottie(译)-看看Airbnb的工程师怎么说。本文分析主要Lottie把json文件转为动画的思路和源码实现。

文章首先介绍Lottie的基本使用,然后分析把json文件映射到动画的实现思路,最后分析Lottie的源码实现,这里分析的是Lottie-Android。

基本用法

与使用相关的只有三个类文件:LottieAnimationView、LottieComposition、LottieDrawable,所以Lottie使用起来特别简单(需要注意Lottie支持API16及以上)。
最简单的使用方式是在xml中增加LottieAnimationView:

"Logo/LogoSmall.json"是需要加载的动画数据路径,根目录是assets目录。

也可以通过代码设置动画数据json路径:

然后在代码中控制动画播放或者添加监听事件:

Lottie提供了LottieDrawable可以使用:


可以看到Lottie使用起来非常简单,我们之后就从以上用到的LottieAnimationView、LottieComposition、LottieDrawable入手来分析下Lottie动画的实现原理。

思路分析

我们先从底层思考下如何在屏幕上绘制动画,最简单的方式是把动画分为多张图片,然后通过周期替换屏幕上绘制的图片来形成动画,这种暴力的方式非常简单,但缺点明显,很耗内存,动画播放中前后两张替换的图片在很多元素并没有变化,重复的内容浪费了空间。

为了提高空间利用率,可以把图片中的元素进行拆分,使用过photoshop的同学知道,其实在处理一张图片时,可以把一张复杂的图片使用多个图层来表示,每个图层上展示一部分内容,图层中的内容也可以拆分为多个元素。拆分元素之后,根据动画需求,可以单独对图层,甚至图层中的元素设置平移、旋转、收缩等动画。

Lottie使用json文件来作为动画数据源,json文件是通过Bodymovin插件导出的,查看sample中给出的json文件,其实就是把图片中的元素进行来拆分,并且描述每个元素的动画执行路径和执行时间。Lottie的功能就是读取这些数据,然后绘制到屏幕上。

现在思考如果我们拿到一份json格式动画如何展示到屏幕上。首先要解析json,建立数据到对象的映射,然后根据数据对象创建合适的Drawable绘制到View上,动画的实现可以通过操作读取到的元素完成。

源码分析

1. json文件到对象的映射

Lottie使用LottieComposition来作为After Effects的数据对象,即把json文件映射到LottieCompositionLottieComposition中提供了解析json的静态方法:

我们看下LottieComposition都有哪些成员变量,这些成员变量描述了After Effects中的动画。

可以看到startFrame、endFrame、duration、scale等都是动画中常见的。我们看下List<Layer>,看名字就是映射拆分后的图层数据:

Layer 中完成layer的json数据解析:

2. 数据对象到Drawable的映射

AnimatableLayer 继承自 Drawable,我们看下它的子类:

其中LayerView对应着Layer数据,Layer中有


对应的LayerView中有

可以简单地理解为ViewGroup中可以包含ViewGroup或者View,但其实整个Lottie实现的动画都是绘制在一个View LottieAnimationView上。

AnimatableLayer 的其它子类如 ShapeLayer,RectLayouer等作为 LayerViewList<AnimatableLayer>的元素。

3. 绘制

LottieAnimationView 继承自 AppCompatImageView,封装了一些动画的操作,如:

具体的绘制时委托为 LottieDrawable 完成的,我们看下 LottieDrawable 中的 draw() 方法:

LottieDrawable 继承自AnimatableLayer,其draw()方法如下:

可以看到先绘制了本层的内容,然后开始绘制包含的layers的内容:

这个过程于界面中ViewGroup嵌套绘制类似。

实现分析

上面我们根据动画绘制的思路分析了下Lottie实现机制,下面从正面来捋一下程序的执行过程:

  1. 创建LottieAnimationView lottieAnimationView
  2. 创建LottieDrawable lottieDrawable
  3. 使用LottieComposition中的静态方法解析json文件创建LottieComposition lottieComposition,这个过程中已经创建来多个Layer对象。
  4. lottieDrawable.setComposition(lottieComposition)

先清理之前的数据,然后开始buildLayersForComposition,即根据lottieComposition建立多个layerView,此时已经创建好了多个Drawable,并通过List建立的为以lottieDrawable为根的一个drawable树。

  1. lottieAnimationView.setImageDrawable(lottieDrawable)

  2. lottieAnimationView.playAnimation()

直接委托给了lottieDrawablelottieDrawable中有private final ValueAnimator animator = ValueAnimator.ofFloat(0f, 1f);

重点看下setProgress方法

调用了private final List<KeyframeAnimation<?>> animations = new ArrayList<>()setProgress

onValueChanged时,各个创建好的Drawable会根据需求进行重绘,达到动画的效果。

Lottie把动画从View的动效转移到了Drawable上。

Lottie的性能

可以看到Lottie把json描述的动画数据映射到Drawable之后,实现动画时用到了ValueAnimator,在动画更新时使用Drawable而非View,个人感觉在不需要交互时Drawable显然比View更加轻量。以下是Lottie性能的官方的说明:

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

欢迎关注公众号wutongke,每天推送移动开发前沿技术文章:

wutongke

推荐阅读:

Airbnb开源炫酷动画库Lottie(译)-看看Airbnb的工程师怎么说

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 202,607评论 5 476
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 85,047评论 2 379
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 149,496评论 0 335
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,405评论 1 273
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,400评论 5 364
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,479评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,883评论 3 395
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,535评论 0 256
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,743评论 1 295
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,544评论 2 319
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,612评论 1 329
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,309评论 4 318
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,881评论 3 306
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,891评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,136评论 1 259
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 42,783评论 2 349
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,316评论 2 342

推荐阅读更多精彩内容