Lottie 源码浅探

本贴最后更新于 1923 天前,其中的信息可能已经物是人非

前置知识:

Lottie 对动画的变换主要是通过 Matrix 实现, 因此需要了解 Matrix 相关知识,可以参考下面的博客:

https://blog.csdn.net/pathuang68/article/details/6991867

一、动画 Json 传入方法:

Json 动画传入的方式定义在 LottieComposition.Factory 中,分别为:

1.      fromAssetFileName:从 assets 中加载,参数 filename 指 assets 中 json 的名称

2.      fromRawFile:从 res/raw 中加载,参数 resId 就是 raw 中 json 的 id

3.      fromInputStream:从 InputStream 中加载

4.      fromJsonString:从 Json 字串中加载

5.      fromJsonReader:从 JsonReader 中加载(如果需要解析的是 JsonObject,可以通过 new JsonReader(newStringReader(jsonObject))的方式加载,不过这种方式不推荐)

二、解析(Lottie 中的解析方法都位于 com.airbnb.lottie.parser 下):

1.      动画 json 传入后,最终会调用到 LottieComposition.Factory.fromJsonReader 方法,该方法会将解析事件委托给 AsyncCompositionLoader 异步线程,而 AsyncCompositionLoader 会调用 LottieComposition.Factory.fromJsonSync 方法,该方法调用 LottieCompositionParser.parse 方法开始进行动画 json 的解析,并将解析结果生成 LottieComposition。

2.      AsyncCompositionLoader 中会调用 LottieCompositionParser.parse 方法进行具体解析,在 LottieCompositionParser.parse 方法中:

1)      layers 会调用 parseLayers,parseLayers 调用 LayerParser.parse 解析

2)      assets 会调用 parseAssets 解析

3)      fonts 会调用 parseFonts,parseFonts 调用 FontParser.parse 解析

4)      chars 会调用 parseChars,parseChars 调用 FontCharacterParser.parse 解析

5)      w 会解析成 width

6)      h 会解析成 height

7)      ip 会解析成 startFrame

8)      op 会解析成 endFrame

9)      fr 会解析成 frameRate(动画速率)

10)  v 会解析成 version(插件版本),进行版本支持性校验

3.      LottieCompositionParser 会根据上面的解析结果生成 LottieComposition:

1)      Rect bounds:通过 scaledWidth(width 与屏幕密度的乘积)、scaledHeight(height 与屏幕密度的乘积)确定

2)      startFrame、endFrame、frameRate 就是上一步解析的值

3)      layers、layerMap 是 layers 中解析的值

4)      precomps 是 assets 中解析的值

5)      images 是 assets 中解析出的图片的值

6)      characters 是 chars 中解析的值

7)      fonts 是 fonts 中解析的值

三、Layer 具体解析:

LayerParser 解析 layers 中的内容,其中关键是 ks,ks 的内容包含了动画用到的一些值,ks 的解析是通过 AnimatableTransformParser.parse 方法进行,LayerParser.parse 会通过上面解析的数据最终生成 Layer:

1)      nm:解析为 layerName

2)      ind:解析为 layerId

3)      refId:解析为 refId

4)      ty:解析为 layerType(附录 1)

5)      parent:解析为 parentId

6)      sw:解析为 solidWidth

7)      sh:解析为 solidHeight

8)      sc:解析为 solidColor

9)      ks:解析为 transform

10)  tt:解析为 mattType

11)  masksProperties:数组,里面数据会通过 MaskParser.parse 解析成 Mask 并存入 masks 中

12)  shapes:数组,里面数据会通过 ContentModelParser.parse 解析成 ContentModel 并存入 shapes 中

13)  t:文本,会进一步解析:

(1)    d:通过 AnimatableValueParser.parseDocumentData 解析成 text

(2)    a:通过 AnimatableTextPropertiesParser.parse 方法解析成 textProperties

14)  ef:Lottie 中目前不支持这种方式的效果,如果要用,需要将这种效果之直接添加到 shape 的 contents 中

15)  sr:解析成 timeStretch

16)  st:解析成 startFrame

17)  w:解析成 preCompWidth

18)  h:解析成 preCompHeight

19)  ip:解析成 inFrame

20)  op:解析成 outFrame

21)  tm:解析成 timeRemapping

22)  cl:解析成 cl

AnimatableTransformParser 解析 ks 内容,包括”a”(位置信息)、”p”(位移)、”s”(缩放)、”rz”(控制 3d 图层,暂时不支持)、”r”、”o”、”so”、”eo”,其实质是解析上面各字段下配置的 k 的值(解析方法为 KeyframesParser.parse),并根据 k 的值生成对应的 AnimatableValue,而 k 值用到的几个主要解析方式是:

1)      PathParser:用于解析 k 数组,解析成 PointF,用于生成 AnimatablePathValue。

2)      FloatParser:用于解析 k 数字,解析成 Float,用于生成 AnimatableFloatValue。

3)      IntegerParser:用于解析 k 数字,解析成 Integer,用于生成 AnimatableIntegerValue。

4)      ScaleXYParser:用于解析 k 数组,解析成 ScaleXY,用于生成 AnimatableScaleValue。

AnimatableTransformParser 中具体字段解析:

1)      a(位置信息):调用 AnimatablePathValueParser.parse 解析 k 中配置的值,并将生成的结果赋值给 anchorPoint

2)      p(位移信息):调用 AnimatablePathValueParser.parseSplitPath 解析,并将解析生成的结果赋值给 postion

3)      s(缩放信息):调用 AnimatableValueParser.parseScale 解析,并将解析结果赋值给 scale

4)      rz:3D 图层,不支持

5)      r(翻转信息):调用 AnimatableValueParser.parseFloat 解析,并将解析结果赋值给 rotation

6)      o(不透明度):调用 AnimatableValueParser.parseFloat 方法解析,并将解析结果赋值给 opacity

7)      so(开始时不透明度):调用 AnimatableValueParser.parseFloat 方法解析,并将解析结果赋值给 startOpacity

8)      eo(结束时不透明度):调用 AnimatableValueParser.parseFloat 方法解析,并将解析结果赋值给 endOpacity

四、AnimatablePathValueParser、AnimatableValueParser 重要方法解析:

1.      AnimatablePathValueParser 中:

1)      parse:若待解析的 JsonReader 中的值是数组,将调用 PathKeyframeParser.parse 方法解析并将解析结果存入 keyframes(List<Keyframe>)中,之后会调用 KeyframesParser.setEndFrames 将 keyframes 中 Keyframe 的 startFrame 与 endFrame 依次串联起来;若不是数组,就直接调用 JsonUtils.jsonToPoint 方法,并将解析结果生成的 Keyframe 存入 keyframes 中。最后根据 keyframes 生成 AnimatablePathValue。

2)      parseSplitPath:

(1)    k:调用 AnimatablePathValueParser.parse 解析,并将解析结果赋值给 pathAnimation

(2)    x:若 JsonReader 值为 String,不解析,并将 hasExpressions 置为 true;若不是 String,则调用 AnimatableValueParser.parseFloat 解析,并将结果返回给 xAnimation

(3)    y:若 JsonReader 值为 String,不解析,并将 hasExpressions 置为 true;若不是 String,则调用 AnimatableValueParser.parseFloat 解析,并将结果返回给 yAnimation

如果 pathAnimation 不为空(k 直接成功),则直接返回 pathAnimation;否则返回通过 xAnimation、yAnimation 生成的 AnimatableSplitDimensPathValue。

2.      AnimatableValueParser 中 parseFloat、parseInteger、parsePoint 等方法中最终会调用 parse 方法解析,而 parse 方法则调用 KeyframesParser.parse 解析,:

1)      parseFloat:调用 parse 并将 FloatParser 作为最终解析方法,解析结果生成 AnimatableFloatValue

2)      parseInteger:调用 parse 并将 IntegerParser 作为最终解析方法,解析结果生成 AnimatableIntegerValue

3)      parsePoint:调用 parse 并将 PointFParser 作为最终解析方法,解析结果生成 AnimatablePointValue

4)      parseScale:调用 parse 并将 ScaleXYParser 作为最终解析方法,解析结果生成 AnimatableScaleValue

5)      parseShapeData:调用 parse 并将 ShapeDataParser 作为最终解析方法,解析结果生成 AnimatableShapeValue

6)      parseDocumentData:调用 parse 并将 DocumentDataParser 作为最终解析方法,解析结果生成 AnimatableTextFrame

7)      parseColor:调用 parse 并将 ColorParser 作为最终解析方法,解析结果生成 AnimatableColorValue

8)      parseGradientColor(渐变色):调用 parse 并将 GradientColorParser 作为最终解析方法,解析结果生成 AnimatableGradientColorValue

3.      KeyframesParser.parse:

Parse 方法中首先会判断 JsonReader 值是否为 STRING 类型,如果是就直接返回空数据集 keyframes(List<Keyframe>),若不是,则解析 JsonReader 中 k 中配置的值,k 中的值基本是 3 种类型,分别是数字数组、对象数组、对象:

1)      若 k 中值为数组,则判断数组中元素是否为数字,若是,则调用 KeyframeParser.parse 解析整个数组(animated 为 false);若不是数字,则调用 KeyframeParser.parse 依次解析数组中元素(animated 为 true)

2)      若 k 中值不是数组,则调用 KeyframeParser.parse 解析整个数组(animated 为 false)

上面的解析结果都会存入 keyframes 中,最后会调用 setEndFrames 方法将 keyframes 中的 Keyframe 的 startFrame 与 endFrame 串联,最后将 keyframes 返回给调用者

4.      KeyframeParser:

1)      parse:判断传入的 animated 值,若为 true,则调用 parseKeyframe;否则调用 parseStaticValue

2)      parseKeyframe 方法是用于解析 k 中对象数组中的值:

(1)    t:解析成 startFrame

(2)    s:调用传入的最终解析方法解析成 stratValue

(3)    e:调用传入的最终解析方法解析成 endValue

(4)    o:调用 JsonUtils.jsonToPoint 解析成 cp1

(5)    i:调用 JsonUtils.JsonToPoint 解析成 cp2

(6)    h:解析成 hold(若 h 值为 1 则为 true,否则为 false)

(7)    to:调用 JsonUtils.jsonToPoint 解析成 pathCp1

(8)    ti:调用 JsonUtils.jsonToPoint 解析成 pathCp2

解析完后会对解析的值进行进一步操作:

(1)    若 hold 为 ture,将 endValue 设置为 startValue,并将 interpolator 设置为 LINEAR_INTERPOLATOR;

(2)    若 hold 为 false,并且 cp1 与 cp2 均不为空,则通过 MiscUtils.clamp 筛选出合适的 cp1、cp2 的值,并通过 PathInterpolatorCompat.create 创建 interpolator(其中有 PathInterpolator 缓存的逻辑,想了解的可以看下)。

(3)    若为其他情况,则将 interpolator 设置为 LINEAR_INTERPOLATOR

最后,通过解析的 startValue、endValue、startFrame 以及创建的 interpolator 生成 Keyframe 并返回给调用者

3)      parseStaticValue:调用传入的最终解析方法解析出 value,并根据 value 生成 Keyframe

5.      FloatParser.parse:

调用 JsonUtils.valueFromObject 解析出 float 值并将结果与传入的 scale 相乘后返回

6.      IntegerParser.parse:

调用调用 JsonUtils.valueFromObject 解析出 float 值并将结果与传入的 scale 相乘,再通过 Math.round 取出对应整值并返回

7.      PointFParser.parse:

如果 reader 中值是 NUMBER 类型,直接通过 JsonReader.nextDouble 与 scale 生成 PointF;若 reader 为数组或对象,则调用 JsonUtils.jsonToPoint 方法解析出 PointF

8.      ScaleXYParser.parse:

若是数组,就调用 JsonReader.beginArray 开始解析,否则就直接解析。解析 JsonReader 中连续的两个 double 值,并根据这两个值与 scale 生成 ScaleXY

9.      ShapeDataParser.parse:

(1)    c:解析成 closed

(2)    v:调用 JsonUitls.jsonToPoints 方法解析成 pointsArray

(3)    i:调用 JsonUtils.jsonToPoints 方法解析成 inTangents

(4)    o:调用 JsonUtils.jsonToPoints 方法解析成 outTangents

若解析的 pointsArray 为空,就构建空的 ShapeData

若 pointsArray 不为空,则先取出 pointsArray 中第一个值,将其设置为 initialPoint。然后从第二个值开始循环 pointsArray 中的值,在 for 循环中,根据 pointsArray 中当前的值与 inTangents 中当前值生成 shapeCp1,pointsArray 中前一个值与 outTangents 中前一个值生成 shapeCp2,然后通过 shapeCp1 与 shapeCp2 以及 pointsArray 当前值生成 CubicCurveData 并加入 curves 中。

然后判断 closed 的值,如果 closed 为 true(标志这个 Shape 是封闭图形),则将 pointsArray 第一值作为当前值,最后一个值作为前一个值,进行上一步 for 循环中的操作,并将生成的 CubicCurveData 也填入 curves 中。

最后根据 initialPoint、closed、curves 生成 ShapeData。

10.  DocumentDataParser.parse:

(1)    t:解析成 text

(2)    f:解析成 fontName

(3)    s:解析成 size

(4)    j:解析成 justification

(5)    tr:解析成 tracking

(6)    lh:解析成 lineHeight

(7)    ls:解析成 baselineShift

(8)    fc:调用 JsonUtils.jsonToColor 解析成 fillColor

(9)    sc:调用 JsonUtils.jsonToColor 解析成 strokeColor

(10) sw:解析成 strokeWidth

(11) of:解析成 strokeOverFill

最后通过上面解析的值生成 DocumentData

11.  ColorParser.parse:

若是数组,调用 JsonReader.beginArray 开始解析,否则直接解析。取 JsonReader 中连续的 4 个 double 值,分别解析成 r、g、b、a,若这四个值都小于,则说明它们被配置成色值的比例,将它们分别乘以 255,最后调用 Color.argb 生成颜色值(int 类型)

12.  GradientColorParser.parse:

若为数组,则调用 reader.beginArray 开始解析,否则直接解析。将 JsonReader 中全部的 double 值解析出并存入 array 中,然后将 array 中的数据每 4 个进行循环,这四个值中第一个存入 positions,第二、三、四分别为 r、g、b 值,通过 r、g、b 生成 color 值后存入 colors,之后通过 positions、colors 值生成 gradientColor 并通过 addOpacityStopsToGradientIfNeeded 设置透明度(透明度是存放在颜色值的后面,不被 4 整除的部分),最后将 gradientColor 返回给调用者

13.  JsonUtils:

1)      jsonToColor:取出传入的 JsonReader 中连续的 3 个 double 值,分别设置为 r、g、b,然后调用 Color.argb 方法生成颜色值

2)      jsonToPoints:若是数组,循环数组中的值,调用 jsonToPoint 解析出 PointF 并存入 points,最后将 points 返回

3)      jsonToPoint:

(1)    若传入的 JsonReader 为数字,调用 jsonNumbersToPoint 并返回

(2)    若传入的 JsonReader 为数组,调用 jsonArrayToPoint 并返回

(3)    若传入的 JsonReader 为对象,调用 jsonObjectToPoint 并返回

4)      jsonNumbersToPoint:

取出 JsonReader 连续的两个 double 值,分别置成 x、y,通过 x、y、scale 生成 PointF

5)      jsonArrayToPoint:

取出数组中连续的两个 double 值,分别置成 x、y,通过 x、y、scale 生成 PointF

6)      jsonObjectToPoint:

取出对象中的 x 值置为 x,对象中的 y 值置为 y,通过 x、y、scale 生成 PointF

7)      valueFromObject:

若传入的 JsonReader 为数字,返回第一个 double 值;若是数组,返回数组中第一个值。

五、Shape 解析(layers 中的 shapes):

LayerParser 会调用 ContentModelParser.parse 将 shapes 数组依次解析成 ContentModel 并存入 shapes 中。

ContentModleParser.parse 首先会解析 ty,之后根据 ty 解析成不同的 ContentModel(附录 2)。

1.      gr:调用 ShapeGroupParser.parse 解析成 ShapeGroup,这个是 Shape 组,里面会包含各种子 Shape。

ShapeGroupParser 中会调用 ContentModelParser.parse 将 it 数组中的内容依次解析成 ContentModel,并存入 items 中。

2.      st:调用 ShapeStrokeParser.parse 解析成 ShapeStroke,Shape 线条的信息。

ShapeStrokeParser.parse 中会解析如下字段:

1)      nm:name

2)      c:color,调用 AnimatableValueParser.parseColor 解析

3)      w:width,调用 AnimatableValueParser.parseFloat 解析

4)      o:opacity,调用 AnimatableValueParser.parseInteger 解析

5)      lc:capType,ShapeStroke.LineCapType 类型(附录 3)

6)      lj:joinType,ShapeStroke.LineJoinType 类型(附录 4)

7)      d:数组,首先会解析 n、v(v 会解析成 val):

(1)    n 为 o,将 val 赋值给 offset

(2)    n 为 d 或 g,将 val 添加到 lineDashPattern 中

解析完,如果 lineDashPattern 的个数如果是 1,就将该数据再加入 lineDashPattern 一次

                   最后会根据上面解析出的值生成 ShapeStroke

3.      gs:调用 GradientStrokeParser.parse 解析成 GradientStroke

1)      nm:name

2)      o:opacity

3)      t:gradientType,渐变类型(GradientType.Linear 或 GradientTypeRadial)

4)      s:startPoint

5)      e:endPoint

6)      w:width

7)      lc:capType

8)      lj:joinType

9)      d:lineDashPattern 或 offset

10)  g:会进一步解析 g 中的字段

(1)    p:points

(2)    k:color,调用 AnimatableValueParser.parseGradientColor 并传入 points 解析

最后根据解析的值生成 GradientStroke

4.      fl:调用 ShapeFillParser.parse 解析成 ShapeFill

1)      nm:name

2)      c:color

3)      o:opacity

4)      fillEnabled:fillEnabled,boolean 类型

5)      r:fillTypeInt,int 类型,之后会根据 fillTypeInt 的值设置 fillType

最后根据解析的值生成 ShapeFill

5.      gf:调用 GradientFillParser.parse 解析成 GradientFill

1)      nm:name

2)      g:color,AnimatableGradientColorValue 类型

3)      o:opacity

4)      t:gradientType

5)      s:startPoint

6)      e:endPoint

7)      r:fillType

6.      tr:调用 AnimatableTransFormParser.parse 解析成 AnimatableTransform

7.      sh:调用 ShapePathParser.parse 解析成 ShapePath,Shape 的绘制路径

1)      nm:name

2)      ind:ind

3)      ks:shape,调用 AnimatableValueParser.parseShapeData 解析

8.      el:调用 CircleShapeParser.parse 解析成 CirCleShape

1)      nm:name

2)      p:position

3)      s:size

4)      d:reversed,boolean 类型,通过 d 的值是否为 3 确定

9.      rc:调用 RectangleShapeParser.parse 解析成 RectangleShape

1)      nm:name

2)      p:position

3)      s:size

4)      r:roundedness,调用 AnimatableValueParser.parseFloat 解析

10.  tm:调用 ShapeTrimPathParser.parse 解析成 ShapeTrimPath

1)      s:start,AnimatableValueParser.parseFloat 解析

2)      e:end,AnimatableValueParser.parseFloat 解析

3)      o:offset,AnimatableValueParser.parseFloat 解析

4)      nm:name

5)      m:type,ShapeTrimPath.Type 类型,附录 5

11.  sr:调用 PolystarShapeParser.parse 解析成 PolystarShape

1)      nm:name

2)      sy:type,PolystarShape.Type,附录 6

3)      pt:points

4)      p:position

5)      r:totation

6)      or:outerRadius,AnimatableValueParser.parseFloat 解析

7)      os:outerRoundedness,AnimatableValueParser.parseFloat 解析

8)      ir:innerRadius,AnimatableValueParser.parseFloat 解析

9)      is:innerRoundedness,AnimatableValueParser.parseFloat 解析

12.  mm:调用 MergePathParser.parse 解析成 MergePath,只支持 KitKat 及之后的版本

1)      nm:name

2)      mode:mode,MergePaths.MergePathsMode 类型,附录 7

13.  rp:调用 RepeaterParser.parse 解析成 Repeater

1)      nm:name

2)      c:copies,AnimatableValueParser.parseFloat 解析

3)      o:offset,AnimatableValueParser.parseFloat 解析

4)      tr:transform

六、Assets 解析(assets):

LottieCompositionParser 中调用 parseAssets 方法解析 assets 中的内容。assets 中内容分两类,一类是图层信息,一类是图片信息:

1.      图层信息:

图层信息解析出的内容存放在 precomps 中,主要解析 id 与 layers 的内容。

assets 中的 layers 与外层的 layers 一样是调用 LayerParser.parse 解析

2.      图片信息(解析出的值会生成 LottieImageAsset 并存入 images 中):

1)      id:解析成 id

2)      w:解析成 width

3)      h:解析成 height

4)      u:解析成 relativeFolder

5)      p:解析成 imageFileName

七、LottieDrawable 构建:

动画 json 解析完成后,会生成 LottieComposition,onCompositionLoaded 方法中将 LottieComposition 设置给 LottieDrawable。

LottieDrawable 的 setComposition 方法会通过 buildCompositionLayer、animator.setComposition、setScale、updateBounds、等方法重新构建 LottieDrawable

buildCompositionLayer 会重新构建 LottieDrawable 中的 CompositionLayer。

animator.setComposition 会重置 animator(LottieValueAnimator)minFrame、maxFrame、frame 与 lastFrameTimeNs 的信息,minFrame 会取原 minFrame 与 composition 的 startFrame(json 中的 ip 值)中的最大值,maxFrame 会取原 maxFrame 与 composition 的 endFrame(json 中的 op 值)中的最小值,frame 会通过一系列比较获取。

八、CompositionLayer 构建:

CompositionLayer 中存储了动画的所有层级的信息。

它在 LottieDrawable 中构建

compositionLayer = new CompositionLayer(this,LayerParser.parse(composition), composition.getLayers(), composition);

LayerParser.parse(composition)构建了最外层的 Layer,高和宽是 json 最外层的 h、w 的值。

composition.getLayers()获取的是 json 最外层 layers 中的内容。

CompositionLayer 的构造方法会遍历传入的 composition.getLayers 中的 Layer,并将 Layer 通过 BaseLayer.forModel 方法,根据 layerType 生成不同的 BaseLayer,对应关系如下表:

layerType

BaseLayer 子类

备注

Shape(ty=4)

ShapeLayer

 

PreComp(ty =0)

CompositionLayer

也就是 Assets 中的内容

Solid(ty=1)

SolidLayer

 

Image(ty=2)

ImageLayer

 

Null(ty=3)

NullLayer

 

Text(ty=5)

TextLayer

 

Unknown/default

null

 

生成的 BaseLayer 会存入 layerMap。

之后会根据前一个 BaseLayer 的 matteType(配置文件中的 tt)值判断是否为 mattedLayer,若是 MattedLayer 就将当前的 BaseLayer 设置为前一个 BaseLayer 的 MatteLayer,否则就添加到 layers 最前的位置。下表标识该 layer 是否为 MattedLayer

matteType

是否为 MattedLayer

 

Add/Invert(tt=1 或 2)

 

其他情况

 

最后会遍历 layerMap,找到每一个 BaseLayer 的 parentLayer(根据 BaseLayer 的 parentId 查找,parentId 对应于 json 中的 parent),并设置到该 BaseLayer 中。

九、绘制:

Json 解析完成后,会调用 setComposition 方法,该方法中会重新设置 LottieComposition,并调用 LottieDrawable.setComposition 方法刷新 LottieDrawable。

上面步骤完成后会调用 setImageDrawable、requestLayout 方法重绘 LottieAnimationView。

LottieAnimationView 的 onDraw 方法会调用 LottieDrawable 的 draw 方法(详细步骤需要查看 ImageView 的绘制流程)。下面具体分析下 LottieDrawable 的 draw 流程。

首先会确定动画的 scale,scale 是通过动画外层的 width 与 height 与 canvas 的比例确定,取高、宽比例中较小的值。若 scale 后的动画大于 canvas,会调用 canvas 的 translate、scale 方法重置 canvas。然后会将获取的 scale 填入矩阵 matrix。

之后会调用 BaseLayer.draw 方法绘制动画中的全部 Layer,具体步骤如下:

BaseLayer.draw()方法中有三个参数,分别是 canvas(画布)、parentMatrix(最外层动画 Matrix)、parentAlpha(最外层透明度)。

1.      调用 buildParentLayerListIfNeeded 方法构建出 CompositionLayer 的全部 parentLayer 并添加到 parentLayers 中。

2.      会将 parentMatrix 设置为 matrix,并将 parentLayers 中的 Layer 动画 Matrix 与传入的 matrix 相乘。

3.      然后,根据传入的 parentAlpha 与该 BaseLayer 的 transform 的 opacity 属性(即 json 中{“layers”:[{“ks” : {“o”: {…}}}]}中从 o 中解析出来的值,一般就是 o 中 k 的值)计算出透明度

4.      如果该 BaseLayer 没有 matteLayer 与 mask,就将 canvas、matrix、alpha 传入 drawLayer 方法进一步绘制,LottieDrawalbe 中调用的是 CompositionLayer 的 drawLayer 方法

十、CompositionLayer 绘制:

1.      CompositionLayer 的 drawLayer 中首先保存 canvas 状态, 然后通过构建时生成的 Layer 的宽、高(具体分析见 CompositionLayer 构建)设置到 newClipRect(RectF 类型),然后将 newClipRect 按上步传入的 parentMatrix 进行变换。

2.      之后,会循环 CompositionLayer 中的 layers(见 CompositionLayer 的构建),并调用对应 BaseLayer 的 draw 方法将所有 layer 依次绘制出来,同时,会按照 newClipRect 重新裁剪 canva(这段代码可能会造成动画显示不全)。最后,将 canvas 设置回之前的状态

十一、      ShapeLayer 构建:

CompostionLayer 构造方法中遍历解析出的 layers(见三),通过 BaseLayer.forModel 根据生成对应的 BaseLayer。
ShapeLayer 对应的 type 为 4,BaseLayer.forModel 直接调用 ShapeLayer 构造函数生成 ShapeLayer。

ShapeLayer 构造函数首先通过 Layer 中的 shapes(List)构建 shapeGroup,然后通过 shapeGroup 构建出 contentGroup。(lottie 文件中也可能会配置 ShapeGroup 即”gr”。之后会调用 ContentGroup 的 setContents,setContents 会循环 ContentGroup 中的 contents,并调用 Content.setContents 方法将其前后的 contents 传入。
ContentGroup 构造方法中:

1.      通过 contentsFromModels 方法将 Layer 中的 shapes(即 List)通过 toContent 方法全部转换成对应的 Content(附录 2),并存入 contents 中

2.      通过 findTransform 方法取出该 ShapeLayer 对应的 AnimatableTransform(”tr”),然后通过 AnimatableTransform 生成 TransformKeyframeAnimation 赋值给 transformAnimation

3.      找出 contents 中全部的 GreedyContent 子类存入 greedyContents 中,并对 greedyContents 进行进一步操作。

十二、      ShapeLayer 绘制:

ShapeLayer 的 drawLayer 会委托给 contentGroup.draw 方法进行。contentGroup.draw 中首先会处理动画与透明度,之后遍历构造时生成的 contents,调用其中 DrawingContent 的子类的 draw 进行实际的绘制。

DrawingContent 子类见附录 2

DrawingConteng 实际上有 3 种:

1.      画线图(StrokeContent、GradientStrokeContent):

StrokeContent 与 GradientStrokeContent 的区别是线条的颜色是否为渐变色。

2.      画填充图(FillContent、GradientFillContent):

FillContent 与 GradientFillContent 的区别是填充色是否为渐变色。

3.      画需要重复的图(RepeaterContent):

十三、      StrokeContent 构建与绘制:

StrokeContent 是 BaseStrokeContent 的子类,大部分逻辑都在 BaseStrokeContent 中,以下是 BaseStrokeContent 的分析:

1.      构造函数:

构造函数中主要是完成的 Paint 以及 Animation 的初始化。

2.      setContents:

setContents 会遍历与其同处一个 ContentGroup 下的其他 Content,找出其中的 TrimPathContent 与其对应的全部 PathContent,然后生成 PathGroup 并存入 pathGroups 中

3.      draw:

1)      通过配置中的 width、color、alpha 对 paint 进行设置。

2)      遍历 pathGroups,对每一个 PathGroup 分情况处理:

(1)    若 pathGroup 中 trimPath 不为空,则调用 applyTrimPath 方法,applyTrimPath 方法中会通过 PathMeasure 根据 TrimPathContent 对 PathContent 构成的 Path 进行处理。

PathMeasure 用法可以参考:

https://blog.csdn.net/u013831257/article/details/51565591

(2)    若 pathGroup 中 trimPath 为空,直接通过 canvas.drawPath 绘制 PathContent 构成的 Path

十四、      动画过程:

Lottie 动画过程是通过 LottieValueAnimator 进行控制,animator 中会维护两个容器,一个存放向 Lottie 中注册的全部 ValueAnimator.AnimatorUpdateListener,一个存放向 Lottie 中注册的全部 AnimatorListener。

ValueAnimator.AnimatorUpdateListener 用于动画更新的监听,回调接口是 onAnimationUpdate。

AnimatorListener 用于动画开始、结束、取消、重复等监听。

LottieDrawable 构建的时候会向 animator 注册一个 AnimatorUpdateListener 的监听,这个监听会收到 animator 发送的动画更新的回调,在此回调中调用 compositionLayer 设置 progress 实现动画的更新。

十五、      附录:

1.      LayerType 含义:

LayerType 对应于 json 文件 layers 中的 ty 字段,映射关系如下:

ty

 

LayerType

含义

0

 

PreComp

 

1

 

Solid

 

2

 

Image

图片

3

 

Null

 

4

 

Shape

绘制形状

5

 

Text

文字

6

 

Unknown

 

 

2.      ContentModel(shape)中 type 含义,标黄的是 DrawingConent 的子类:

ty

ContentModel

toContent 类型

备注

gr

ShapeGroup

ContentGroup

Shape 组,里面包含子 Shape

st

ShapeStroke

StrokeContent

Shape 描边信息,如线的颜色、宽度、透明度等

gs

GradientStroke

GradientStrokeContent

 

fl

ShapeFill

FillContent

 

gf

GradientFill

GradientFillContent

 

tr

AnimatableTransform

null

Shape 的动画

sh

ShapePath

ShapeContent

Shape 的绘制路径

el

CircleShape

EllipseContent

 

rc

RectangleShape

RectangleContent

 

tm

ShapeTrimPath

TrimPathContent

修剪路径

sr

PolystarShape

PolystarContent

 

mm

MergePaths

MergePathsContent

 

rp

Repeater

RepeaterContent

 

 

3.      ShapeStroke.LineCapType 类型:

index

ShapeStroke.LineCapType 类型

Paint.Cap(线帽子)

0

LineCapType.Butt

Paint.Cap.BUTT 无线帽

1

LineCapType.Round

Paint.Cap.ROUND 圆线帽

2

LineCapType.Unknown

Paint.Cap.SQUARE 方线帽

 

4.      ShapeStroke.LineJoinType 类型:

index

ShapeStroke.LineJoinType 类型

Paint.Join(线段连接样式)

0

Miter

Paint.Join.MITER 锐角连接

1

Round

Paint.Join.ROUND 圆弧连接

2

Bevel

Paint.Join.BEVEL 斜接

 

5.      ShapeTrimPath.Type 类型:

id

ShapeTrimPath.Type 类型

备注

1

Simultaneously

 

2

Individually

 

 

6.      PolystarShape.Type 类型:

value

PolystarShape.Type 类型

备注

1

Star

 

2

Polygon

 

                  

7.      MergePaths.MergePathsMode 类型:

id

MergePaths.MergePathsMode

备注

1

Merge

 

2

Add

 

3

Subtract

 

4

Intersect

 

5

ExcludeIntersections

  • lottie
    1 引用
  • Android

    Android 是一种以 Linux 为基础的开放源码操作系统,主要使用于便携设备。2005 年由 Google 收购注资,并拉拢多家制造商组成开放手机联盟开发改良,逐渐扩展到到平板电脑及其他领域上。

    331 引用 • 315 回帖 • 83 关注

相关帖子

欢迎来到这里!

我们正在构建一个小众社区,大家在这里相互信任,以平等 • 自由 • 奔放的价值观进行分享交流。最终,希望大家能够找到与自己志同道合的伙伴,共同成长。

注册 关于
请输入回帖内容 ...

推荐标签 标签

  • 大数据

    大数据(big data)是指无法在一定时间范围内用常规软件工具进行捕捉、管理和处理的数据集合,是需要新处理模式才能具有更强的决策力、洞察发现力和流程优化能力的海量、高增长率和多样化的信息资产。

    89 引用 • 113 回帖
  • CAP

    CAP 指的是在一个分布式系统中, Consistency(一致性)、 Availability(可用性)、Partition tolerance(分区容错性),三者不可兼得。

    11 引用 • 5 回帖 • 553 关注
  • 百度

    百度(Nasdaq:BIDU)是全球最大的中文搜索引擎、最大的中文网站。2000 年 1 月由李彦宏创立于北京中关村,致力于向人们提供“简单,可依赖”的信息获取方式。“百度”二字源于中国宋朝词人辛弃疾的《青玉案·元夕》词句“众里寻他千百度”,象征着百度对中文信息检索技术的执著追求。

    63 引用 • 785 回帖 • 249 关注
  • Sphinx

    Sphinx 是一个基于 SQL 的全文检索引擎,可以结合 MySQL、PostgreSQL 做全文搜索,它可以提供比数据库本身更专业的搜索功能,使得应用程序更容易实现专业化的全文检索。

    1 引用 • 171 关注
  • Spring

    Spring 是一个开源框架,是于 2003 年兴起的一个轻量级的 Java 开发框架,由 Rod Johnson 在其著作《Expert One-On-One J2EE Development and Design》中阐述的部分理念和原型衍生而来。它是为了解决企业应用开发的复杂性而创建的。框架的主要优势之一就是其分层架构,分层架构允许使用者选择使用哪一个组件,同时为 JavaEE 应用程序开发提供集成的框架。

    938 引用 • 1456 回帖 • 163 关注
  • Swift

    Swift 是苹果于 2014 年 WWDC(苹果开发者大会)发布的开发语言,可与 Objective-C 共同运行于 Mac OS 和 iOS 平台,用于搭建基于苹果平台的应用程序。

    34 引用 • 37 回帖 • 495 关注
  • RIP

    愿逝者安息!

    8 引用 • 92 回帖 • 286 关注
  • webpack

    webpack 是一个用于前端开发的模块加载器和打包工具,它能把各种资源,例如 JS、CSS(less/sass)、图片等都作为模块来使用和处理。

    41 引用 • 130 回帖 • 294 关注
  • 锤子科技

    锤子科技(Smartisan)成立于 2012 年 5 月,是一家制造移动互联网终端设备的公司,公司的使命是用完美主义的工匠精神,打造用户体验一流的数码消费类产品(智能手机为主),改善人们的生活质量。

    4 引用 • 31 回帖 • 6 关注
  • 智能合约

    智能合约(Smart contract)是一种旨在以信息化方式传播、验证或执行合同的计算机协议。智能合约允许在没有第三方的情况下进行可信交易,这些交易可追踪且不可逆转。智能合约概念于 1994 年由 Nick Szabo 首次提出。

    1 引用 • 11 回帖 • 6 关注
  • uTools

    uTools 是一个极简、插件化、跨平台的现代桌面软件。通过自由选配丰富的插件,打造你得心应手的工具集合。

    5 引用 • 13 回帖
  • Jenkins

    Jenkins 是一套开源的持续集成工具。它提供了非常丰富的插件,让构建、部署、自动化集成项目变得简单易用。

    51 引用 • 37 回帖
  • Vditor

    Vditor 是一款浏览器端的 Markdown 编辑器,支持所见即所得、即时渲染(类似 Typora)和分屏预览模式。它使用 TypeScript 实现,支持原生 JavaScript、Vue、React 和 Angular。

    308 引用 • 1658 回帖 • 1 关注
  • Solidity

    Solidity 是一种智能合约高级语言,运行在 [以太坊] 虚拟机(EVM)之上。它的语法接近于 JavaScript,是一种面向对象的语言。

    3 引用 • 18 回帖 • 346 关注
  • 脑图

    脑图又叫思维导图,是表达发散性思维的有效图形思维工具 ,它简单却又很有效,是一种实用性的思维工具。

    21 引用 • 58 回帖 • 1 关注
  • Kotlin

    Kotlin 是一种在 Java 虚拟机上运行的静态类型编程语言,由 JetBrains 设计开发并开源。Kotlin 可以编译成 Java 字节码,也可以编译成 JavaScript,方便在没有 JVM 的设备上运行。在 Google I/O 2017 中,Google 宣布 Kotlin 成为 Android 官方开发语言。

    19 引用 • 33 回帖 • 20 关注
  • Latke

    Latke 是一款以 JSON 为主的 Java Web 框架。

    70 引用 • 532 回帖 • 706 关注
  • 服务器

    服务器,也称伺服器,是提供计算服务的设备。由于服务器需要响应服务请求,并进行处理,因此一般来说服务器应具备承担服务并且保障服务的能力。

    124 引用 • 580 回帖
  • PostgreSQL

    PostgreSQL 是一款功能强大的企业级数据库系统,在 BSD 开源许可证下发布。

    21 引用 • 22 回帖 • 1 关注
  • VirtualBox

    VirtualBox 是一款开源虚拟机软件,最早由德国 Innotek 公司开发,由 Sun Microsystems 公司出品的软件,使用 Qt 编写,在 Sun 被 Oracle 收购后正式更名成 Oracle VM VirtualBox。

    10 引用 • 2 回帖 • 1 关注
  • 博客

    记录并分享人生的经历。

    270 引用 • 2386 回帖
  • QQ

    1999 年 2 月腾讯正式推出“腾讯 QQ”,在线用户由 1999 年的 2 人(马化腾和张志东)到现在已经发展到上亿用户了,在线人数超过一亿,是目前使用最广泛的聊天软件之一。

    45 引用 • 557 回帖 • 224 关注
  • CentOS

    CentOS(Community Enterprise Operating System)是 Linux 发行版之一,它是来自于 Red Hat Enterprise Linux 依照开放源代码规定释出的源代码所编译而成。由于出自同样的源代码,因此有些要求高度稳定的服务器以 CentOS 替代商业版的 Red Hat Enterprise Linux 使用。两者的不同在于 CentOS 并不包含封闭源代码软件。

    238 引用 • 224 回帖
  • MySQL

    MySQL 是一个关系型数据库管理系统,由瑞典 MySQL AB 公司开发,目前属于 Oracle 公司。MySQL 是最流行的关系型数据库管理系统之一。

    673 引用 • 535 回帖
  • IPFS

    IPFS(InterPlanetary File System,星际文件系统)是永久的、去中心化保存和共享文件的方法,这是一种内容可寻址、版本化、点对点超媒体的分布式协议。请浏览 IPFS 入门笔记了解更多细节。

    20 引用 • 245 回帖 • 232 关注
  • GitBook

    GitBook 使您的团队可以轻松编写和维护高质量的文档。 分享知识,提高团队的工作效率,让用户满意。

    3 引用 • 8 回帖 • 1 关注
  • 机器学习

    机器学习(Machine Learning)是一门多领域交叉学科,涉及概率论、统计学、逼近论、凸分析、算法复杂度理论等多门学科。专门研究计算机怎样模拟或实现人类的学习行为,以获取新的知识或技能,重新组织已有的知识结构使之不断改善自身的性能。

    76 引用 • 37 回帖