Lottie 源码浅探

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

前置知识:

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 回帖 • 82 关注

相关帖子

欢迎来到这里!

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

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

推荐标签 标签

  • ZeroNet

    ZeroNet 是一个基于比特币加密技术和 BT 网络技术的去中心化的、开放开源的网络和交流系统。

    1 引用 • 21 回帖 • 591 关注
  • Logseq

    Logseq 是一个隐私优先、开源的知识库工具。

    Logseq is a joyful, open-source outliner that works on top of local plain-text Markdown and Org-mode files. Use it to write, organize and share your thoughts, keep your to-do list, and build your own digital garden.

    4 引用 • 55 回帖 • 6 关注
  • 机器学习

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

    76 引用 • 37 回帖
  • 国际化

    i18n(其来源是英文单词 internationalization 的首末字符 i 和 n,18 为中间的字符数)是“国际化”的简称。对程序来说,国际化是指在不修改代码的情况下,能根据不同语言及地区显示相应的界面。

    7 引用 • 26 回帖 • 1 关注
  • Q&A

    提问之前请先看《提问的智慧》,好的问题比好的答案更有价值。

    6364 引用 • 28618 回帖 • 264 关注
  • Kotlin

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

    19 引用 • 33 回帖 • 22 关注
  • uTools

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

    5 引用 • 13 回帖
  • jsDelivr

    jsDelivr 是一个开源的 CDN 服务,可为 npm 包、GitHub 仓库提供免费、快速并且可靠的全球 CDN 加速服务。

    5 引用 • 31 回帖 • 33 关注
  • PHP

    PHP(Hypertext Preprocessor)是一种开源脚本语言。语法吸收了 C 语言、 Java 和 Perl 的特点,主要适用于 Web 开发领域,据说是世界上最好的编程语言。

    164 引用 • 407 回帖 • 523 关注
  • 程序员

    程序员是从事程序开发、程序维护的专业人员。

    529 引用 • 3527 回帖
  • 创造

    你创造的作品可能会帮助到很多人,如果是开源项目的话就更赞了!

    171 引用 • 988 回帖
  • abitmean

    有点意思就行了

    14 关注
  • IDEA

    IDEA 全称 IntelliJ IDEA,是一款 Java 语言开发的集成环境,在业界被公认为最好的 Java 开发工具之一。IDEA 是 JetBrains 公司的产品,这家公司总部位于捷克共和国的首都布拉格,开发人员以严谨著称的东欧程序员为主。

    180 引用 • 400 回帖
  • Bug

    Bug 本意是指臭虫、缺陷、损坏、犯贫、窃听器、小虫等。现在人们把在程序中一些缺陷或问题统称为 bug(漏洞)。

    76 引用 • 1738 回帖 • 2 关注
  • Sillot

    Sillot (汐洛)孵化自思源笔记,致力于服务智慧新彖乄,具有彖乄驱动、极致优雅、开发者友好的特点
    Github 地址:https://github.com/Hi-Windom/Sillot

    12 引用 • 26 关注
  • InfluxDB

    InfluxDB 是一个开源的没有外部依赖的时间序列数据库。适用于记录度量,事件及实时分析。

    2 引用 • 47 关注
  • Dubbo

    Dubbo 是一个分布式服务框架,致力于提供高性能和透明化的 RPC 远程服务调用方案,是 [阿里巴巴] SOA 服务化治理方案的核心框架,每天为 2,000+ 个服务提供 3,000,000,000+ 次访问量支持,并被广泛应用于阿里巴巴集团的各成员站点。

    60 引用 • 82 回帖 • 590 关注
  • 支付宝

    支付宝是全球领先的独立第三方支付平台,致力于为广大用户提供安全快速的电子支付/网上支付/安全支付/手机支付体验,及转账收款/水电煤缴费/信用卡还款/AA 收款等生活服务应用。

    29 引用 • 347 回帖 • 2 关注
  • 分享

    有什么新发现就分享给大家吧!

    240 引用 • 1729 回帖
  • 房星科技

    房星网,我们不和没有钱的程序员谈理想,我们要让程序员又有理想又有钱。我们有雄厚的房地产行业线下资源,遍布昆明全城的 100 家门店、四千地产经纪人是我们坚实的后盾。

    6 引用 • 141 回帖 • 548 关注
  • Hprose

    Hprose 是一款先进的轻量级、跨语言、跨平台、无侵入式、高性能动态远程对象调用引擎库。它不仅简单易用,而且功能强大。你无需专门学习,只需看上几眼,就能用它轻松构建分布式应用系统。

    9 引用 • 17 回帖 • 591 关注
  • 禅道

    禅道是一款国产的开源项目管理软件,她的核心管理思想基于敏捷方法 scrum,内置了产品管理和项目管理,同时又根据国内研发现状补充了测试管理、计划管理、发布管理、文档管理、事务管理等功能,在一个软件中就可以将软件研发中的需求、任务、bug、用例、计划、发布等要素有序的跟踪管理起来,完整地覆盖了项目管理的核心流程。

    5 引用 • 15 回帖 • 223 关注
  • 链书

    链书(Chainbook)是 B3log 开源社区提供的区块链纸质书交易平台,通过 B3T 实现共享激励与价值链。可将你的闲置书籍上架到链书,我们共同构建这个全新的交易平台,让闲置书籍继续发挥它的价值。

    链书社

    链书目前已经下线,也许以后还有计划重制上线。

    14 引用 • 257 回帖 • 2 关注
  • Openfire

    Openfire 是开源的、基于可拓展通讯和表示协议 (XMPP)、采用 Java 编程语言开发的实时协作服务器。Openfire 的效率很高,单台服务器可支持上万并发用户。

    6 引用 • 7 回帖 • 87 关注
  • IBM

    IBM(国际商业机器公司)或万国商业机器公司,简称 IBM(International Business Machines Corporation),总公司在纽约州阿蒙克市。1911 年托马斯·沃森创立于美国,是全球最大的信息技术和业务解决方案公司,拥有全球雇员 30 多万人,业务遍及 160 多个国家和地区。

    16 引用 • 53 回帖 • 104 关注
  • 京东

    京东是中国最大的自营式电商企业,2015 年第一季度在中国自营式 B2C 电商市场的占有率为 56.3%。2014 年 5 月,京东在美国纳斯达克证券交易所正式挂牌上市(股票代码:JD),是中国第一个成功赴美上市的大型综合型电商平台,与腾讯、百度等中国互联网巨头共同跻身全球前十大互联网公司排行榜。

    14 引用 • 102 回帖 • 405 关注
  • 又拍云

    又拍云是国内领先的 CDN 服务提供商,国家工信部认证通过的“可信云”,乌云众测平台认证的“安全云”,为移动时代的创业者提供新一代的 CDN 加速服务。

    21 引用 • 37 回帖 • 505 关注