Lottie动画的优劣及原理

2023-05-16

前言

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

Lottie实现

  • Lottie通过读取json文件信息实现动画效果。
  • json信息包括json整体结构、图片资源、图层信息等,这些属性阐述了动画该做什么、该怎么做。
  • json文件解析:
    在这里插入图片描述

Lottie动画总体实现原理

在这里插入图片描述

  • 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);
  1. 从源码可以看到LottieComposition被传给了LottieDrawer。
public void setComposition(@NonNull LottieComposition composition){
   if (L.DBG) {
        Log.v(TAG, "Set Composition \n" + composition);
    }
    lottieDrawable.setCallback(this);
    
    this.composition = composition;
    boolean isNewComposition = lottieDrawable.setComposition(composition);
}
  1. 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);
    }
  1. 构造CompositionLayer时,遍历LottieComposition数据对象中所有的Layer数据,并将其转换为BaseLayer图层对象。
public CompositionLayer(LottieDrawable lottieDrawable, Layer layerModel, List<Layer> layerModels,
                        LottieComposition composition) {
       super(lottieDrawable, layerModel);

        LongSparseArray<BaseLayer> 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的关系。
在这里插入图片描述
5. BaseLayer 的 draw 绘制过程中,会调用抽象方法 drawLayer,各个继承的子类会具体实现。

@SuppressLint("WrongConstant") 
@Override
public void draw(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;
   }
 }
  1. 如果Composition包含多个子图层,会遍历子图层,调用各自的draw方法进行绘制。
@Override
void drawLayer(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 < animations.size(); i++) {
      animations.get(i).setProgress(progress);
    }
  }

//BaseKeyframeAnimation
  void setProgress(@FloatRange(from = 0f, to = 1f) float progress) {
    if (progress < getStartDelayProgress()) {
      progress = 0f;
    } else if (progress > getEndProgress()) {
      progress = 1f;
    }

    if (progress == this.progress) {
      return;
    }
    this.progress = progress;

    for (int i = 0; i < listeners.size(); i++) {
      listeners.get(i).onValueChanged();
    }
  }

//BaseLayer
  @Override public void onValueChanged() {
    invalidateSelf();
  }

//BaseLayer
  private void invalidateSelf() {
    lottieDrawable.invalidateSelf();
  }

Lottie的优劣

优点:

  • 支持跨平台,开发成本较低,一套Lottie动画可以在Android/IOS/Web多端使用。
  • 性能好,端上除了解析json,基本没有其他耗性能的操作;并且相比于需要存储较多图片的帧动画,Lottie可以节省比较多的内存空间。
  • 可以从服务端配置URL实现,不需要APP发版就可以实现更新。

不足点:

  • Lottie动画不能进行交互。
  • Lottie动画端上也无法进行编辑。
  • Lottie不支持加载文字。
  • Lottie不支持压缩位图,如果使用png等位图,需要自行在tiny等压缩平台进行图片压缩、降低包体积。
  • Lottie存在mask、matters 时,性能会受到较大影响。

mask(掩膜):

  • 用选定的图形或物体,对要处理的图像进行遮挡,控制图像的处理区域或处理过程。

Matte(前景蒙版):

  • 前背景分离的结果,是一个灰度图,灰度图上的每个像素点的灰度值代表原始图像每个像素属于前景的程度。
  • 白色代表某一个像素属于前景。
  • 黑色代表某个像素属于背景。

Mask和Matte的区别:

  • Mask是Matte的一种特例。
  • Mask只有两种透明度,1和0,即完全透明和完全不透明。Mask是为了去除合成的锯齿而设计,不过锯齿没了,合成痕迹明显,显得不真实。
  • Matte有很多层次的透明度。图像中每个像素都有自己的透明度,这些像素的透明度可以合成、融合,使图片看起来真实自然。

可以在源码中查看Mask、Matte的计算过程。
在这里插入图片描述

  • 如果没有Mask或Matte,直接调用drawLayer返回。
  • 如果存在Mask或Matte,需要先saveLayer,再调用drawLayer返回。
  • saveLayer是一个耗时的操作,需要先分配、绘制一个offscreen的缓冲区,这增加了渲染的时间。

Lottie的使用

加载动画资源的方式

  • src/main/res/raw 中的 json 动画。
  • src/main/assets 中的 json 文件。
  • src/main/assets 中的 zip 文件。
  • json 或 zip 文件的 Url。
  • json 或 zip 文件的 InputStream。
  • json 字符串。

xml文件使用Lottie

<com.airbnb.lottie.LottieAnimationView
        android:id="@+id/animation_view"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"

        app:lottie_rawRes="@raw/hello_world"
        // or
        app:lottie_fileName="hello_world.json"

        app:lottie_loop="true"
        app:lottie_autoPlay="true" />

xml文件中Lottie各属性
在这里插入图片描述
代码中使用Lottie

LottieAnimationView animationView = ...

animationView.setAnimation(R.raw.hello_world);
// or
animationView.setAnimation(R.raw.hello_world.json);

animationView.playAnimation();

代码中Lottie各属性
在这里插入图片描述

Lottie动态配置

lottieAnimationView.setAnimationFromUrl(url); 
lottieAnimationView.playAnimation();

更多技术文章欢迎关注公众号度熊君。
在这里插入图片描述

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

Lottie动画的优劣及原理 的相关文章

  • main函数参数及可变参数列表解析

    main函数的参数解析 平时写main函数大多都是int main xff0c 然后误以为main函数是没有参数的 xff0c 但在有些书上又看到main括号里有三个不怎么看的懂的参数 xff0c 其实仔细研究就会发现 xff0c main
  • 给定一棵二叉树,判断它是否是镜像对称的

    给定一个二叉树 xff0c 检查它是否是镜像对称的 在这里先解释一下镜像对称的概念 xff0c 顾名思义 xff0c 就像人站在镜子前面面对自己一样 xff0c 看到的一切都是对称的 镜中的反射与现实中的人具有相同的头部 xff0c 但反射
  • 判断一棵二叉树是否是平衡二叉树

    我们先来整理一下什么是平衡二叉树 xff1f 满足以下两点的就是平衡二叉树 xff1a 1 左右子树的高度差不能超过1 2 左右子树也是平衡二叉树 需要注意的是空树也是平衡二叉树 例如下面这棵树就不是平衡二叉树 因为对于B来说左右子树高度超
  • Fiddler实现手机抓包入门配置

    emmm 在此之前我也只是听过抓包抓包 xff0c 但是没有真正的接触到 xff0c Fiddler是一款很强大的抓包工具了 xff0c 但是一开始很多人可能会想我一样走上歧途 xff0c 所以在此总结一下 xff0c 希望看到的朋友少走弯
  • Idea使用Maven连接MySQL数据库

    连接MySQL数据库的步骤 xff1a 1 添加数据库 2 填写连接信息如图 3 填写连接信息完之后 xff0c 需要记一下jar包的位置 xff0c 用于添加jar包 4 显示jar包信息 5 添加文件夹命名java xff0c 并将其设
  • mybatis generator 生成中文字段注释

    首先 xff0c 试了好几次 xff0c 重写 DefaultCommentGenerator xff0c 创建MyCommentGenerator类实现CommentGenerator接口 xff0c 都是按照网上的方案来的 xff0c
  • org/apache/velocity/context/Context

    mybatisplus generator AutoGenerator 生成文件时报错如下 xff1a 请输入表名 xff1a alarm 14 38 28 569 main DEBUG com baomidou mybatisplus g
  • NVIDIA开源NeMo,CUDA,pytorch安装使用过程踩坑总结

    说明 Nemo cpu版本直接看文章最后一部分 xff08 linux版本的 xff09 xff0c windows下安装的话要先安装CUDA来支持GPU xff0c 在安装pytorch 在安装nemo 但是还是出问题了 xff0c 不建
  • 网络url转文件

    首先是获取springboot resource下面的文件 String fileUrl 61 ResourceUtils getURL 34 classpath 34 getPath 43 34 test txt 34 File file
  • ByteBuffer和String互转

    Charset charset 61 StandardCharsets UTF 8 String str 61 34 kj你好呀 kjlkjlkjlk 34 ByteBuffer buffer 61 ByteBuffer allocate
  • @Test单元测试注入bean 3种方式

    1 64 ContextConfiguration 64 ContextConfiguration Spring整合JUnit4测试时 xff0c 使用注解引入多个配置文件 64 RunWith SpringJUnit4ClassRunne
  • java 导出word

    目前来看 xff0c java导出word大致有6种解决方案 xff1a 1 xff1a Jacob是Java COM Bridge的缩写 xff0c 使用Jacob自带的DLL动态链接库 xff0c 并通过JNI的方式实现了在Java平台
  • JAVA visualVM 查看堆栈分配

    由于在做nlp xff0c 用到standFord nlp xff0c 堆栈分配总是一处 xff0c 所以需要这个 xff0c 正好学习一下 我用的是jdk8 5 xff0c 位于 JDK 根目录的 bin 文件夹下 xff0c 无需安装
  • Android Studio Gradle失败 Could not reolve play-services-vision-17.0.2.aar 等类似问题解决办法

    Android Studio Gradle失败 Could not reolve play services vision 17 0 2 aar 等类似问题解决办法 网上一些常规解决办法 如果上述常规解决办法尝试后 xff0c 都无法解决你
  • 利用fsl进行配准

    利用fsl进行配准 配准概念 配准就是将两个不同空间 体素 xff0c 扫描的位置不一致的nii xff0c 配准到同一个空间上 xff0c 使得两者在大脑上的相应位置就可以一一对应上了 通常MRI数据处理的步骤 xff1a 先配准到tem
  • 1.计算机概论

    学习linux前先来了解一下计算机概念 xff0c 如果了解相关内容 xff0c 可跳过本章节 1 1 电脑 电脑是一种计算机 xff0c 计算机实际是 xff1a 接受用户输入的命令与数据 xff0c 经由中央处理器的算术和逻辑单元运算后
  • SpringDataJPA——使用EntityManager利用原生SQL自定义复杂查询

    使用EntityManager 原生SQL查询方法记录以下学习过程中找到的其他文章地址 原生SQL查询方法 在这里进行记录以下使用过程 xff0c 注释已经很清晰 span class token annotation punctuatio
  • 操作系统(二十三)生产者消费者问题

    2 3 6 生产者消费者问题 生产者消费者问题 The proceducer consumer problem 是一个经典的进程同步的问题 xff0c 问题是这样描述的 xff1a 在操作系统中有一组生产者进程一组消费者进程 xff0c 生
  • Powershell脚本:一键优化windows 10(原版)

    本套Powershell脚本出自github开源项目 xff0c 包含原版WIN10系统大概300个一键优化 组件精简方案 例如彻底关闭Windows defender xff0c 关闭共享 打印机 xff0c 保留Windows upda
  • 安装ubuntu与windows双系统

    ubuntu程序的安装 开机进bios xff0c 在Security页面 xff0c 关掉secure boot xff1a 存储系统文件 xff0c 建议10GB 15GB xff1b swap xff1a 交换分区 xff0c 即Li

随机推荐

  • Windows编程经典书籍

    本人是刚刚开始学习windows编程的 感觉看雪学院的大牛很NB 想找一些书籍来看学习学习 可是不知道看哪些书好 驱动 对菜鸟们来说真是一个很深奥的话题 所以 我找来了这篇文章供大家分享 以后大家发现什么好书就在楼下跟贴吧 作者 xff1a
  • 经典Windows编程书单

    说好的这次写一个图形编程书单 但是看起来不是很好整理 xff0c 这类书散落的家里到处都是 先把经典Windows编程的书整理一下吧 xff0c 不过Windows的也到处都是很多都找不到了 xff0c 只能把找到的拍个照 xff0c 可能
  • ubuntu18.04开机循环输入密码无法进入桌面

    问题 xff1a 在profile和environment文件里配置了java环境变量后 xff0c 重启电脑后即使输入正确的用户名和密码 xff0c 也会重新跳到登录界面 xff0c 无法进入系统 xff0c 一直循环登录 原因 xff1
  • ubuntu 安装VS

    Table of Contents 一 前言 二 安装过程 1 下载VS Code 2 安装过程 3 下载C 43 43 模块 4 汉化 5 常用快捷键 一 前言 因为要用到在ubuntu系统中使用VS Code 来编写C 43 43 代码
  • Windows系统FTP服务器设置

    设置操作步骤 步骤一 xff1a 确认电脑是否开通联网共享服务 依次点击 控制面板 程序 启用或关闭Windows功能 按钮 xff0c 进入到 Windows功能 页面 xff0c 查看 Internet Information Serv
  • springboot thymeleaf 配置

    Springboot默认是不支持JSP的 xff0c 默认使用thymeleaf模板引擎 1 在application properties文件中增加Thymeleaf模板的配置 thymelea模板配置 spring thymeleaf
  • 【ubuntu】fatal: detected dubious ownership in repository at ...

    在ubuntu使用git的时候遇到了以下错误 xff1a fatal detected dubious ownership in repository at 39 home xxx 39 To add an exception for th
  • 有意思的Windows脚本(1)

    有意思的Windows脚本 1 因为不知道今天的博客写什么啦 xff0c 就放几个好玩的Windows脚本的源码吧 xff0c 大家千万不要干坏事情哦 xff0c 嘿嘿 1 vbs循环 xff08 桌面上建一个记事本 xff0c 输入下面代
  • 程序员3年5年10年三个阶段

    第一阶段 三年 三年对于程序员来说是第一个门槛 xff0c 这个阶段将会淘汰掉一批不适合写代码的人 这一阶段 xff0c 我们走出校园 xff0c 迈入社会 xff0c 成为一名程序员 xff0c 正式从书本上的内容迈向真正的企业级开发 我
  • 使用 matplotlib 轻松制作动画

    https www codenong com e264872efa062c7d6955 该链接讲了如何使用 matplotlib 轻松制作动画 xff0c 很好用
  • C#中使用IMemoryCache实现内存缓存

    1 缓存基础知识 缓存是实际工作中非常常用的一种提高性能的方法 缓存可以减少生成内容所需的工作 xff0c 从而显著提高应用程序的性能和可伸缩性 缓存最适用于不经常更改的数据 通过缓存 xff0c 可以比从原始数据源返回的数据的副本速度快得
  • 2021-09-13使用@Slf4j报错 程序包org.slf4j不存在

    导入两个maven依赖 然后就OK了 span class token tag span class token tag span class token punctuation lt span dependency span span c
  • PowerShell7.X的安装与美化

    参考链接1 xff1a https blog csdn net qq 39537898 article details 117411132参考链接2 xff1a https sspai com post 59380 很有参考价值 xff0c
  • Lab2 p3 围棋吃子的算法实现

    简单介绍下框架 xff1a 1 xff0e 声明一维数组block 作为一个临时变量记录一个块的大小 xff0c 声明一个整型blockLength记录这个块的长度 2 xff0e kill 为吃子的主函数 recersion int i
  • Python爬取皮皮虾视频

    背景 xff1a 今天闲着没事做 xff0c 然后想着刷刷视频 xff0c 然后发现前段时间学习了一下网络爬虫的一些基本应用 xff0c 就想着利用爬虫到网上去爬取一点视频来模拟人为的点击 下载操作 因为皮皮虾是手机端的app xff0c
  • 解决Result Maps collection already contains value for...BaseResultMap问题

    使用generatorSqlmapCustom逆向工程生成代码报错 假如使用generatorSqlmapCustom逆向工程生成代码 xff0c 即生成dao文件和mapper xml文件 xff0c 复制粘贴至工程中运行报错 Resul
  • IDEA2022.1的一些不常见问题解决方案

    文章目录 IDEA2022 1小问题解决方案 学习的时候尝鲜用了最新版本的IDEA 出现过以下老版本不会遇见的问题 Spring Initializer 创建的项目 无法新建module 显示Directory is already tak
  • 史上最全,Android P指纹应用及源码解析

    简单使用 源码分析 首先需要了解以下几点 指纹识别相关api是在Android23版本添加的 xff0c 所以过早版本的设备是无法使用的 xff1b android span class token punctuation span os
  • RNA-seq数据分析(HISAT2+featureCounts+StringTie)

    RNA seq数据分析 简介1 生物基础1 1 中心法则1 2 RNA seq Protocol1 3 RNA seq总的路线图 2 数据分析2 1 前期准备2 1 1 创建目录 amp 安装conda2 1 2 常用文件格式简介 2 2
  • Lottie动画的优劣及原理

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