從json文件到炫酷動(dòng)畫-Lottie實(shí)現(xiàn)思路和源碼分析

Lottie是最近Airbnb開源的動(dòng)畫項(xiàng)目瞧预,支持Android即硼、iOS鹿响、ReactNaitve三個(gè)平臺,相關(guān)背景介紹可以參考之前的文章Airbnb開源炫酷動(dòng)畫庫Lottie(譯)-看看Airbnb的工程師怎么說夕春。本文分析主要Lottie把json文件轉(zhuǎn)為動(dòng)畫的思路和源碼實(shí)現(xiàn)。

文章首先介紹Lottie的基本使用专挪,然后分析把json文件映射到動(dòng)畫的實(shí)現(xiàn)思路及志,最后分析Lottie的源碼實(shí)現(xiàn),這里分析的是Lottie-Android寨腔。

基本用法

與使用相關(guān)的只有三個(gè)類文件:LottieAnimationView速侈、LottieComposition、LottieDrawable迫卢,所以Lottie使用起來特別簡單(需要注意Lottie支持API16及以上)倚搬。
最簡單的使用方式是在xml中增加LottieAnimationView:

"Logo/LogoSmall.json"是需要加載的動(dòng)畫數(shù)據(jù)路徑,根目錄是assets目錄乾蛤。

也可以通過代碼設(shè)置動(dòng)畫數(shù)據(jù)json路徑:

然后在代碼中控制動(dòng)畫播放或者添加監(jiān)聽事件:

Lottie提供了LottieDrawable可以使用:


可以看到Lottie使用起來非常簡單每界,我們之后就從以上用到的LottieAnimationView、LottieComposition家卖、LottieDrawable入手來分析下Lottie動(dòng)畫的實(shí)現(xiàn)原理盆犁。

思路分析

我們先從底層思考下如何在屏幕上繪制動(dòng)畫,最簡單的方式是把動(dòng)畫分為多張圖片篡九,然后通過周期替換屏幕上繪制的圖片來形成動(dòng)畫谐岁,這種暴力的方式非常簡單,但缺點(diǎn)明顯,很耗內(nèi)存伊佃,動(dòng)畫播放中前后兩張?zhí)鎿Q的圖片在很多元素并沒有變化窜司,重復(fù)的內(nèi)容浪費(fèi)了空間。

為了提高空間利用率航揉,可以把圖片中的元素進(jìn)行拆分塞祈,使用過photoshop的同學(xué)知道,其實(shí)在處理一張圖片時(shí)帅涂,可以把一張復(fù)雜的圖片使用多個(gè)圖層來表示议薪,每個(gè)圖層上展示一部分內(nèi)容,圖層中的內(nèi)容也可以拆分為多個(gè)元素媳友。拆分元素之后斯议,根據(jù)動(dòng)畫需求,可以單獨(dú)對圖層醇锚,甚至圖層中的元素設(shè)置平移哼御、旋轉(zhuǎn)、收縮等動(dòng)畫焊唬。

Lottie使用json文件來作為動(dòng)畫數(shù)據(jù)源恋昼,json文件是通過Bodymovin插件導(dǎo)出的,查看sample中給出的json文件赶促,其實(shí)就是把圖片中的元素進(jìn)行來拆分液肌,并且描述每個(gè)元素的動(dòng)畫執(zhí)行路徑和執(zhí)行時(shí)間。Lottie的功能就是讀取這些數(shù)據(jù)鸥滨,然后繪制到屏幕上矩屁。

現(xiàn)在思考如果我們拿到一份json格式動(dòng)畫如何展示到屏幕上。首先要解析json爵赵,建立數(shù)據(jù)到對象的映射吝秕,然后根據(jù)數(shù)據(jù)對象創(chuàng)建合適的Drawable繪制到View上,動(dòng)畫的實(shí)現(xiàn)可以通過操作讀取到的元素完成空幻。

源碼分析

1. json文件到對象的映射

Lottie使用LottieComposition來作為After Effects的數(shù)據(jù)對象烁峭,即把json文件映射到LottieCompositionLottieComposition中提供了解析json的靜態(tài)方法:

我們看下LottieComposition都有哪些成員變量秕铛,這些成員變量描述了After Effects中的動(dòng)畫约郁。

可以看到startFrame、endFrame但两、duration鬓梅、scale等都是動(dòng)畫中常見的。我們看下List<Layer>谨湘,看名字就是映射拆分后的圖層數(shù)據(jù):

Layer 中完成layer的json數(shù)據(jù)解析:

2. 數(shù)據(jù)對象到Drawable的映射

AnimatableLayer 繼承自 Drawable绽快,我們看下它的子類:

其中LayerView對應(yīng)著Layer數(shù)據(jù)芥丧,Layer中有


對應(yīng)的LayerView中有

可以簡單地理解為ViewGroup中可以包含ViewGroup或者View,但其實(shí)整個(gè)Lottie實(shí)現(xiàn)的動(dòng)畫都是繪制在一個(gè)View LottieAnimationView上坊罢。

AnimatableLayer 的其它子類如 ShapeLayer续担,RectLayouer等作為 LayerViewList<AnimatableLayer>的元素。

3. 繪制

LottieAnimationView 繼承自 AppCompatImageView活孩,封裝了一些動(dòng)畫的操作物遇,如:

具體的繪制時(shí)委托為 LottieDrawable 完成的,我們看下 LottieDrawable 中的 draw() 方法:

LottieDrawable 繼承自AnimatableLayer憾儒,其draw()方法如下:

可以看到先繪制了本層的內(nèi)容询兴,然后開始繪制包含的layers的內(nèi)容:

這個(gè)過程于界面中ViewGroup嵌套繪制類似。

實(shí)現(xiàn)分析

上面我們根據(jù)動(dòng)畫繪制的思路分析了下Lottie實(shí)現(xiàn)機(jī)制起趾,下面從正面來捋一下程序的執(zhí)行過程:

  1. 創(chuàng)建LottieAnimationView lottieAnimationView
  2. 創(chuàng)建LottieDrawable lottieDrawable
  3. 使用LottieComposition中的靜態(tài)方法解析json文件創(chuàng)建LottieComposition lottieComposition诗舰,這個(gè)過程中已經(jīng)創(chuàng)建來多個(gè)Layer對象。
  4. lottieDrawable.setComposition(lottieComposition)

先清理之前的數(shù)據(jù)阳掐,然后開始buildLayersForComposition始衅,即根據(jù)lottieComposition建立多個(gè)layerView冷蚂,此時(shí)已經(jīng)創(chuàng)建好了多個(gè)Drawable缭保,并通過List建立的為以lottieDrawable為根的一個(gè)drawable樹。

  1. lottieAnimationView.setImageDrawable(lottieDrawable)

  2. lottieAnimationView.playAnimation()

直接委托給了lottieDrawable蝙茶,lottieDrawable中有private final ValueAnimator animator = ValueAnimator.ofFloat(0f, 1f);

重點(diǎn)看下setProgress方法

調(diào)用了private final List<KeyframeAnimation<?>> animations = new ArrayList<>()setProgress

onValueChanged時(shí)艺骂,各個(gè)創(chuàng)建好的Drawable會(huì)根據(jù)需求進(jìn)行重繪,達(dá)到動(dòng)畫的效果隆夯。

Lottie把動(dòng)畫從View的動(dòng)效轉(zhuǎn)移到了Drawable上钳恕。

Lottie的性能

可以看到Lottie把json描述的動(dòng)畫數(shù)據(jù)映射到Drawable之后,實(shí)現(xiàn)動(dòng)畫時(shí)用到了ValueAnimator蹄衷,在動(dòng)畫更新時(shí)使用Drawable而非View忧额,個(gè)人感覺在不需要交互時(shí)Drawable顯然比View更加輕量。以下是Lottie性能的官方的說明:

  1. 如果沒有mask和mattes愧口,那么性能和內(nèi)存非常好睦番,沒有bitmap創(chuàng)建,大部分操作都是簡單的cavas繪制耍属。
  2. 如果存在mattes托嚣,將會(huì)創(chuàng)建2~3個(gè)bitmap。bitmap在動(dòng)畫加載到window時(shí)被創(chuàng)建厚骗,被window刪除時(shí)回收示启。所以不宜在RecyclerView中使用包涵mattes或者mask的動(dòng)畫,否則會(huì)引起bitmap抖動(dòng)领舰。除了內(nèi)存抖動(dòng)夫嗓,mattes和mask中必要的bitmap.eraseColor()和canvas.drawBitmap()也會(huì)降低動(dòng)畫性能迟螺。對于簡單的動(dòng)畫,在實(shí)際使用時(shí)性能不太明顯啤月。
  3. 如果在列表中使用動(dòng)畫煮仇,推薦使用緩存LottieAnimationView.setAnimation(String, CacheStrategy) 。

歡迎關(guān)注公眾號wutongke谎仲,每天推送移動(dòng)開發(fā)前沿技術(shù)文章:

wutongke

推薦閱讀:

Airbnb開源炫酷動(dòng)畫庫Lottie(譯)-看看Airbnb的工程師怎么說

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末浙垫,一起剝皮案震驚了整個(gè)濱河市,隨后出現(xiàn)的幾起案子郑诺,更是在濱河造成了極大的恐慌夹姥,老刑警劉巖,帶你破解...
    沈念sama閱讀 206,378評論 6 481
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件辙诞,死亡現(xiàn)場離奇詭異辙售,居然都是意外死亡,警方通過查閱死者的電腦和手機(jī)飞涂,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 88,356評論 2 382
  • 文/潘曉璐 我一進(jìn)店門旦部,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人较店,你說我怎么就攤上這事士八。” “怎么了梁呈?”我有些...
    開封第一講書人閱讀 152,702評論 0 342
  • 文/不壞的土叔 我叫張陵婚度,是天一觀的道長。 經(jīng)常有香客問我官卡,道長蝗茁,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 55,259評論 1 279
  • 正文 為了忘掉前任寻咒,我火速辦了婚禮哮翘,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘毛秘。我一直安慰自己饭寺,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 64,263評論 5 371
  • 文/花漫 我一把揭開白布熔脂。 她就那樣靜靜地躺著佩研,像睡著了一般。 火紅的嫁衣襯著肌膚如雪霞揉。 梳的紋絲不亂的頭發(fā)上旬薯,一...
    開封第一講書人閱讀 49,036評論 1 285
  • 那天,我揣著相機(jī)與錄音适秩,去河邊找鬼绊序。 笑死硕舆,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的骤公。 我是一名探鬼主播抚官,決...
    沈念sama閱讀 38,349評論 3 400
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼阶捆!你這毒婦竟也來了凌节?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 36,979評論 0 259
  • 序言:老撾萬榮一對情侶失蹤洒试,失蹤者是張志新(化名)和其女友劉穎倍奢,沒想到半個(gè)月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體垒棋,經(jīng)...
    沈念sama閱讀 43,469評論 1 300
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡卒煞,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 35,938評論 2 323
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了叼架。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片畔裕。...
    茶點(diǎn)故事閱讀 38,059評論 1 333
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖乖订,靈堂內(nèi)的尸體忽然破棺而出扮饶,到底是詐尸還是另有隱情,我是刑警寧澤垢粮,帶...
    沈念sama閱讀 33,703評論 4 323
  • 正文 年R本政府宣布贴届,位于F島的核電站靠粪,受9級特大地震影響蜡吧,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜占键,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 39,257評論 3 307
  • 文/蒙蒙 一昔善、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧畔乙,春花似錦君仆、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,262評論 0 19
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至牍鞠,卻和暖如春咖摹,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背难述。 一陣腳步聲響...
    開封第一講書人閱讀 31,485評論 1 262
  • 我被黑心中介騙來泰國打工萤晴, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留吐句,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 45,501評論 2 354
  • 正文 我出身青樓店读,卻偏偏與公主長得像嗦枢,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個(gè)殘疾皇子屯断,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 42,792評論 2 345

推薦閱讀更多精彩內(nèi)容