揭秘Lottie动画的优劣及原理 您所在的位置:网站首页 iosgif 揭秘Lottie动画的优劣及原理

揭秘Lottie动画的优劣及原理

2023-11-07 06:57| 来源: 网络整理| 查看: 265

前言

Lottie是目前应用十分广泛的动画框架。在周会汇报的时候,老板问能不能对Lottie进行优化,于是就有了下文对Lottie原理的研究。毕竟要进行优化,首先要深入了解原理嘛。

Lottie动画

Lottie.gif

Lottie实现 Lottie通过读取json文件信息实现动画效果。 json信息包括动画长度、宽度、动画资源、图层信息等,这些属性阐述了动画该做什么、该怎么做。 json文件解析:

屏幕快照 2021-06-14 下午1.54.17.png

Lottie动画总体实现原理

Lottie动画原理.png

Lottie 先将动画 JSON 文件转换为 LottieComposition 数据对象。 继承 ImageView 的 LottieAnimationView 将数据对象 LottieComposition 和渲染能力委托给 LottieDrawable 处理。 在 LottieDrawable 中会将数据对象 LottieComposition 组建为具有 draw 能力的 BaseLayer,并在 LottieAnimationView 需要绘制时,调用自己和各个层级 BaseLayer 的渲染,从而达到动画效果。

下文将把Lottie原理细分为适配、绘制、动画原理,并辅以源代码进行阐述。

适配原理 对于普通图片,可能会需要2x,3x多份图片资源进行手机适配。 而Lottie本身已经自带了适配的功能:解析json文件时,读取动画的宽、高之后,会乘以手机的密度。在使用的时候判断Lottie动画适配后的宽高是否大于手机实际宽高,如果大于,Lottie会进行缩放。 绘制原理

1、 获取LottieComposition数据对象

//Raw 文件加载 LottieComposition.Factory.fromRawFile(this,R.raw.data, compositionListener); //asset 文件加载  LottieComposition.Factory.fromAssetFileName(this, "data.json", compositionListener); //自定义文件目录加载  LottieComposition.Factory.fromInputStream(new FileInputStream("/sdcard/data/data.json"), compositionListener);

2、 从源码可以看到LottieComposition被传给了LottieDrawer。

publicvoidsetComposition(@NonNull LottieComposition composition){ if (L.DBG) { Log.v(TAG, "Set Composition \n" + composition); } lottieDrawable.setCallback(this); this.composition = composition; boolean isNewComposition = lottieDrawable.setComposition(composition); }

3、 LottieDrawable通过buildComposition方法构造最外层的CompositionLayer。

public boolean setComposition(LottieComposition composition) { if (this.composition == composition) { return false; } clearComposition(); this.composition = composition; buildCompositionLayer(); animator.setComposition(composition); // ...... } private void buildCompositionLayer() { compositionLayer = new CompositionLayer( this, LayerParser.parse(composition), composition.getLayers(), composition); }

4、构造CompositionLayer时,遍历LottieComposition数据对象中所有的Layer数据,并将其转换为BaseLayer图层对象。

public CompositionLayer(LottieDrawable lottieDrawable, Layer layerModel, List layerModels, LottieComposition composition) { super(lottieDrawable, layerModel); LongSparseArray layerMap = new LongSparseArray(composition.getLayers().size()); BaseLayer mattedLayer = null; for (int i = layerModels.size() - 1; i >= 0; i--) { Layer lm = layerModels.get(i); BaseLayer layer = BaseLayer.forModel(lm, lottieDrawable, composition); } }

CompositionLayer与其他BaseLayer的关系类似于ViewGroup与View的关系。

layer关系.png

5、 BaseLayer 的 draw 绘制过程中,会调用抽象方法 drawLayer,各个继承的子类会具体实现。

@SuppressLint("WrongConstant") @Override publicvoiddraw(Canvas canvas, Matrix parentMatrix, int parentAlpha){ if (!hasMatteOnThisLayer() && !hasMasksOnThisLayer()) { matrix.preConcat(transform.getMatrix()); L.beginSection("Layer#drawLayer"); drawLayer(canvas, matrix, alpha); L.endSection("Layer#drawLayer"); recordRenderTime(L.endSection(drawTraceName)); return; } }

6、 如果Composition包含多个子图层,会遍历子图层,调用各自的draw方法进行绘制。

@OverridevoiddrawLayer(Canvas canvas, Matrix parentMatrix, int parentAlpha){ L.beginSection("CompositionLayer#draw"); canvas.save(); newClipRect.set(0, 0, layerModel.getPreCompWidth(), layerModel.getPreCompHeight()); parentMatrix.mapRect(newClipRect); for (int i = layers.size() - 1; i >= 0 ; i--) { boolean nonEmptyClip = true; if (!newClipRect.isEmpty()) { nonEmptyClip = canvas.clipRect(newClipRect); } if (nonEmptyClip) { BaseLayer layer = layers.get(i); layer.draw(canvas, parentMatrix, parentAlpha); } } canvas.restore(); L.endSection("CompositionLayer#draw"); } 动画原理 使用LottieAnimationView.playAnimaiton方法可以执行动画。 在animator执行过程中会逐层回调setprogress方法。 最终触发lottieDrawable的invalidateSelf方法,使lottieDrawable重新绘制。 这样随着animator的进行,lottieDrawable重新绘制,最终形成完整的动画。源码如下。 //LottieDrawable    public void setProgress(@FloatRange(from = 0f, to = 1f) float progress) {     this.progress = progress;     if (compositionLayer != null) {       compositionLayer.setProgress(progress);     }   }   //CompositionLayer   @Override public void setProgress(@FloatRange(from = 0f, to = 1f) float progress) {     super.setProgress(progress);     progress -= layerModel.getStartProgress();     for (int i = layers.size() - 1; i >= 0; i--) {       layers.get(i).setProgress(progress);     }   } //BaseLayer   void setProgress(@FloatRange(from = 0f, to = 1f) float progress) {     //...     for (int i = 0; i 


【本文地址】

公司简介

联系我们

今日新闻

    推荐新闻

    专题文章
      CopyRight 2018-2019 实验室设备网 版权所有