@octane-xplat/lottie 0.9.0 → 0.10.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (313) hide show
  1. package/README.md +36 -16
  2. package/dist/native/index.js +10 -9
  3. package/dist/native/octane-client-build.json +1 -1
  4. package/dist/web/index.js +39 -25
  5. package/dist/web/octane-client-build.json +1 -1
  6. package/package.json +25 -3
  7. package/platforms/macos/LICENSE-EpoxyCore-Apache-2.0.txt +201 -0
  8. package/platforms/macos/LICENSE-LRUCache-MIT.txt +22 -0
  9. package/platforms/macos/LICENSE-Lottie-Apache-2.0.txt +201 -0
  10. package/platforms/macos/LICENSE-ZIPFoundation-MIT.txt +21 -0
  11. package/platforms/macos/UPSTREAM.md +13 -0
  12. package/platforms/macos/resources/PrivacyInfo.xcprivacy +23 -0
  13. package/platforms/macos/resources/THIRD-PARTY-NOTICES.txt +10 -0
  14. package/platforms/macos/src/XplatAppKitLottie.swift +246 -0
  15. package/platforms/macos/src/vendor/Lottie/PrivacyInfo.xcprivacy +23 -0
  16. package/platforms/macos/src/vendor/Lottie/Private/CoreAnimation/Animations/CAAnimation+TimingConfiguration.swift +81 -0
  17. package/platforms/macos/src/vendor/Lottie/Private/CoreAnimation/Animations/CALayer+addAnimation.swift +476 -0
  18. package/platforms/macos/src/vendor/Lottie/Private/CoreAnimation/Animations/CombinedShapeAnimation.swift +92 -0
  19. package/platforms/macos/src/vendor/Lottie/Private/CoreAnimation/Animations/CustomPathAnimation.swift +89 -0
  20. package/platforms/macos/src/vendor/Lottie/Private/CoreAnimation/Animations/DropShadowAnimation.swift +179 -0
  21. package/platforms/macos/src/vendor/Lottie/Private/CoreAnimation/Animations/EllipseAnimation.swift +53 -0
  22. package/platforms/macos/src/vendor/Lottie/Private/CoreAnimation/Animations/GradientAnimations.swift +287 -0
  23. package/platforms/macos/src/vendor/Lottie/Private/CoreAnimation/Animations/LayerProperty.swift +440 -0
  24. package/platforms/macos/src/vendor/Lottie/Private/CoreAnimation/Animations/OpacityAnimation.swift +53 -0
  25. package/platforms/macos/src/vendor/Lottie/Private/CoreAnimation/Animations/RectangleAnimation.swift +59 -0
  26. package/platforms/macos/src/vendor/Lottie/Private/CoreAnimation/Animations/ShapeAnimation.swift +246 -0
  27. package/platforms/macos/src/vendor/Lottie/Private/CoreAnimation/Animations/StarAnimation.swift +119 -0
  28. package/platforms/macos/src/vendor/Lottie/Private/CoreAnimation/Animations/StrokeAnimation.swift +96 -0
  29. package/platforms/macos/src/vendor/Lottie/Private/CoreAnimation/Animations/TransformAnimations.swift +380 -0
  30. package/platforms/macos/src/vendor/Lottie/Private/CoreAnimation/Animations/VisibilityAnimation.swift +63 -0
  31. package/platforms/macos/src/vendor/Lottie/Private/CoreAnimation/CompatibilityTracker.swift +129 -0
  32. package/platforms/macos/src/vendor/Lottie/Private/CoreAnimation/CoreAnimationLayer.swift +645 -0
  33. package/platforms/macos/src/vendor/Lottie/Private/CoreAnimation/Extensions/CALayer+fillBounds.swift +35 -0
  34. package/platforms/macos/src/vendor/Lottie/Private/CoreAnimation/Extensions/KeyframeGroup+exactlyOneKeyframe.swift +28 -0
  35. package/platforms/macos/src/vendor/Lottie/Private/CoreAnimation/Extensions/Keyframes+combined.swift +345 -0
  36. package/platforms/macos/src/vendor/Lottie/Private/CoreAnimation/Extensions/Keyframes+timeRemapping.swift +46 -0
  37. package/platforms/macos/src/vendor/Lottie/Private/CoreAnimation/Layers/AnimationLayer.swift +169 -0
  38. package/platforms/macos/src/vendor/Lottie/Private/CoreAnimation/Layers/BaseAnimationLayer.swift +46 -0
  39. package/platforms/macos/src/vendor/Lottie/Private/CoreAnimation/Layers/BaseCompositionLayer.swift +120 -0
  40. package/platforms/macos/src/vendor/Lottie/Private/CoreAnimation/Layers/CALayer+setupLayerHierarchy.swift +169 -0
  41. package/platforms/macos/src/vendor/Lottie/Private/CoreAnimation/Layers/GradientRenderLayer.swift +101 -0
  42. package/platforms/macos/src/vendor/Lottie/Private/CoreAnimation/Layers/ImageLayer.swift +81 -0
  43. package/platforms/macos/src/vendor/Lottie/Private/CoreAnimation/Layers/InfiniteOpaqueAnimationLayer.swift +57 -0
  44. package/platforms/macos/src/vendor/Lottie/Private/CoreAnimation/Layers/LayerModel+makeAnimationLayer.swift +59 -0
  45. package/platforms/macos/src/vendor/Lottie/Private/CoreAnimation/Layers/MaskCompositionLayer.swift +139 -0
  46. package/platforms/macos/src/vendor/Lottie/Private/CoreAnimation/Layers/PreCompLayer.swift +107 -0
  47. package/platforms/macos/src/vendor/Lottie/Private/CoreAnimation/Layers/RepeaterLayer.swift +113 -0
  48. package/platforms/macos/src/vendor/Lottie/Private/CoreAnimation/Layers/ShapeItemLayer.swift +346 -0
  49. package/platforms/macos/src/vendor/Lottie/Private/CoreAnimation/Layers/ShapeLayer.swift +569 -0
  50. package/platforms/macos/src/vendor/Lottie/Private/CoreAnimation/Layers/SolidLayer.swift +66 -0
  51. package/platforms/macos/src/vendor/Lottie/Private/CoreAnimation/Layers/TextLayer.swift +157 -0
  52. package/platforms/macos/src/vendor/Lottie/Private/CoreAnimation/Layers/TransformLayer.swift +13 -0
  53. package/platforms/macos/src/vendor/Lottie/Private/CoreAnimation/ValueProviderStore.swift +157 -0
  54. package/platforms/macos/src/vendor/Lottie/Private/EmbeddedLibraries/EpoxyCore/Diffing/Collection+Diff.swift +267 -0
  55. package/platforms/macos/src/vendor/Lottie/Private/EmbeddedLibraries/EpoxyCore/Diffing/Diffable.swift +19 -0
  56. package/platforms/macos/src/vendor/Lottie/Private/EmbeddedLibraries/EpoxyCore/Diffing/DiffableSection.swift +16 -0
  57. package/platforms/macos/src/vendor/Lottie/Private/EmbeddedLibraries/EpoxyCore/Diffing/IndexChangeset.swift +187 -0
  58. package/platforms/macos/src/vendor/Lottie/Private/EmbeddedLibraries/EpoxyCore/Diffing/SectionedChangeset.swift +22 -0
  59. package/platforms/macos/src/vendor/Lottie/Private/EmbeddedLibraries/EpoxyCore/Logging/EpoxyLogger.swift +102 -0
  60. package/platforms/macos/src/vendor/Lottie/Private/EmbeddedLibraries/EpoxyCore/Model/CallbackContextEpoxyModeled.swift +8 -0
  61. package/platforms/macos/src/vendor/Lottie/Private/EmbeddedLibraries/EpoxyCore/Model/EpoxyModelArrayBuilder.swift +48 -0
  62. package/platforms/macos/src/vendor/Lottie/Private/EmbeddedLibraries/EpoxyCore/Model/EpoxyModelProperty.swift +158 -0
  63. package/platforms/macos/src/vendor/Lottie/Private/EmbeddedLibraries/EpoxyCore/Model/EpoxyModelStorage.swift +88 -0
  64. package/platforms/macos/src/vendor/Lottie/Private/EmbeddedLibraries/EpoxyCore/Model/EpoxyModeled.swift +54 -0
  65. package/platforms/macos/src/vendor/Lottie/Private/EmbeddedLibraries/EpoxyCore/Model/Internal/AnyEpoxyModelProperty.swift +31 -0
  66. package/platforms/macos/src/vendor/Lottie/Private/EmbeddedLibraries/EpoxyCore/Model/Internal/ClassReference.swift +39 -0
  67. package/platforms/macos/src/vendor/Lottie/Private/EmbeddedLibraries/EpoxyCore/Model/Providers/AnimatedProviding.swift +10 -0
  68. package/platforms/macos/src/vendor/Lottie/Private/EmbeddedLibraries/EpoxyCore/Model/Providers/DataIDProviding.swift +58 -0
  69. package/platforms/macos/src/vendor/Lottie/Private/EmbeddedLibraries/EpoxyCore/Model/Providers/DidDisplayProviding.swift +41 -0
  70. package/platforms/macos/src/vendor/Lottie/Private/EmbeddedLibraries/EpoxyCore/Model/Providers/DidEndDisplayingProviding.swift +42 -0
  71. package/platforms/macos/src/vendor/Lottie/Private/EmbeddedLibraries/EpoxyCore/Model/Providers/DidSelectProviding.swift +36 -0
  72. package/platforms/macos/src/vendor/Lottie/Private/EmbeddedLibraries/EpoxyCore/Model/Providers/ErasedContentProviding.swift +49 -0
  73. package/platforms/macos/src/vendor/Lottie/Private/EmbeddedLibraries/EpoxyCore/Model/Providers/MakeViewProviding.swift +61 -0
  74. package/platforms/macos/src/vendor/Lottie/Private/EmbeddedLibraries/EpoxyCore/Model/Providers/SetBehaviorsProviding.swift +38 -0
  75. package/platforms/macos/src/vendor/Lottie/Private/EmbeddedLibraries/EpoxyCore/Model/Providers/SetContentProviding.swift +38 -0
  76. package/platforms/macos/src/vendor/Lottie/Private/EmbeddedLibraries/EpoxyCore/Model/Providers/StyleIDProviding.swift +38 -0
  77. package/platforms/macos/src/vendor/Lottie/Private/EmbeddedLibraries/EpoxyCore/Model/Providers/TraitCollectionProviding.swift +14 -0
  78. package/platforms/macos/src/vendor/Lottie/Private/EmbeddedLibraries/EpoxyCore/Model/Providers/ViewDifferentiatorProviding.swift +34 -0
  79. package/platforms/macos/src/vendor/Lottie/Private/EmbeddedLibraries/EpoxyCore/Model/Providers/ViewProviding.swift +13 -0
  80. package/platforms/macos/src/vendor/Lottie/Private/EmbeddedLibraries/EpoxyCore/Model/Providers/WillDisplayProviding.swift +41 -0
  81. package/platforms/macos/src/vendor/Lottie/Private/EmbeddedLibraries/EpoxyCore/Model/ViewEpoxyModeled.swift +10 -0
  82. package/platforms/macos/src/vendor/Lottie/Private/EmbeddedLibraries/EpoxyCore/README.md +30 -0
  83. package/platforms/macos/src/vendor/Lottie/Private/EmbeddedLibraries/EpoxyCore/SwiftUI/EpoxySwiftUIIntrinsicContentSizeInvalidator.swift +34 -0
  84. package/platforms/macos/src/vendor/Lottie/Private/EmbeddedLibraries/EpoxyCore/SwiftUI/EpoxySwiftUILayoutMargins.swift +38 -0
  85. package/platforms/macos/src/vendor/Lottie/Private/EmbeddedLibraries/EpoxyCore/SwiftUI/EpoxyableView+SwiftUIView.swift +165 -0
  86. package/platforms/macos/src/vendor/Lottie/Private/EmbeddedLibraries/EpoxyCore/SwiftUI/LayoutUtilities/MeasuringViewRepresentable.swift +127 -0
  87. package/platforms/macos/src/vendor/Lottie/Private/EmbeddedLibraries/EpoxyCore/SwiftUI/LayoutUtilities/SwiftUIMeasurementContainer.swift +468 -0
  88. package/platforms/macos/src/vendor/Lottie/Private/EmbeddedLibraries/EpoxyCore/SwiftUI/SwiftUIView.swift +146 -0
  89. package/platforms/macos/src/vendor/Lottie/Private/EmbeddedLibraries/EpoxyCore/SwiftUI/UIView+SwiftUIView.swift +39 -0
  90. package/platforms/macos/src/vendor/Lottie/Private/EmbeddedLibraries/EpoxyCore/SwiftUI/UIViewConfiguringSwiftUIView.swift +41 -0
  91. package/platforms/macos/src/vendor/Lottie/Private/EmbeddedLibraries/EpoxyCore/Views/BehaviorsConfigurableView.swift +45 -0
  92. package/platforms/macos/src/vendor/Lottie/Private/EmbeddedLibraries/EpoxyCore/Views/ContentConfigurableView.swift +36 -0
  93. package/platforms/macos/src/vendor/Lottie/Private/EmbeddedLibraries/EpoxyCore/Views/EpoxyableView.swift +5 -0
  94. package/platforms/macos/src/vendor/Lottie/Private/EmbeddedLibraries/EpoxyCore/Views/StyledView.swift +43 -0
  95. package/platforms/macos/src/vendor/Lottie/Private/EmbeddedLibraries/EpoxyCore/Views/ViewType.swift +47 -0
  96. package/platforms/macos/src/vendor/Lottie/Private/EmbeddedLibraries/LRUCache/LRUCache.swift +257 -0
  97. package/platforms/macos/src/vendor/Lottie/Private/EmbeddedLibraries/LRUCache/README.md +24 -0
  98. package/platforms/macos/src/vendor/Lottie/Private/EmbeddedLibraries/README.md +43 -0
  99. package/platforms/macos/src/vendor/Lottie/Private/EmbeddedLibraries/ZipFoundation/Archive+BackingConfiguration.swift +149 -0
  100. package/platforms/macos/src/vendor/Lottie/Private/EmbeddedLibraries/ZipFoundation/Archive+Deprecated.swift +23 -0
  101. package/platforms/macos/src/vendor/Lottie/Private/EmbeddedLibraries/ZipFoundation/Archive+Helpers.swift +361 -0
  102. package/platforms/macos/src/vendor/Lottie/Private/EmbeddedLibraries/ZipFoundation/Archive+MemoryFile.swift +205 -0
  103. package/platforms/macos/src/vendor/Lottie/Private/EmbeddedLibraries/ZipFoundation/Archive+Progress.swift +66 -0
  104. package/platforms/macos/src/vendor/Lottie/Private/EmbeddedLibraries/ZipFoundation/Archive+Reading.swift +160 -0
  105. package/platforms/macos/src/vendor/Lottie/Private/EmbeddedLibraries/ZipFoundation/Archive+ReadingDeprecated.swift +50 -0
  106. package/platforms/macos/src/vendor/Lottie/Private/EmbeddedLibraries/ZipFoundation/Archive+Writing.swift +403 -0
  107. package/platforms/macos/src/vendor/Lottie/Private/EmbeddedLibraries/ZipFoundation/Archive+WritingDeprecated.swift +95 -0
  108. package/platforms/macos/src/vendor/Lottie/Private/EmbeddedLibraries/ZipFoundation/Archive+ZIP64.swift +191 -0
  109. package/platforms/macos/src/vendor/Lottie/Private/EmbeddedLibraries/ZipFoundation/Archive.swift +404 -0
  110. package/platforms/macos/src/vendor/Lottie/Private/EmbeddedLibraries/ZipFoundation/Data+Compression.swift +632 -0
  111. package/platforms/macos/src/vendor/Lottie/Private/EmbeddedLibraries/ZipFoundation/Data+CompressionDeprecated.swift +43 -0
  112. package/platforms/macos/src/vendor/Lottie/Private/EmbeddedLibraries/ZipFoundation/Data+Serialization.swift +146 -0
  113. package/platforms/macos/src/vendor/Lottie/Private/EmbeddedLibraries/ZipFoundation/Date+ZIP.swift +101 -0
  114. package/platforms/macos/src/vendor/Lottie/Private/EmbeddedLibraries/ZipFoundation/Entry+Serialization.swift +191 -0
  115. package/platforms/macos/src/vendor/Lottie/Private/EmbeddedLibraries/ZipFoundation/Entry+ZIP64.swift +170 -0
  116. package/platforms/macos/src/vendor/Lottie/Private/EmbeddedLibraries/ZipFoundation/Entry.swift +669 -0
  117. package/platforms/macos/src/vendor/Lottie/Private/EmbeddedLibraries/ZipFoundation/FileManager+ZIP.swift +405 -0
  118. package/platforms/macos/src/vendor/Lottie/Private/EmbeddedLibraries/ZipFoundation/FileManager+ZIPDeprecated.swift +28 -0
  119. package/platforms/macos/src/vendor/Lottie/Private/EmbeddedLibraries/ZipFoundation/README.md +24 -0
  120. package/platforms/macos/src/vendor/Lottie/Private/EmbeddedLibraries/ZipFoundation/URL+ZIP.swift +34 -0
  121. package/platforms/macos/src/vendor/Lottie/Private/MainThread/LayerContainers/CompLayers/CompositionLayer.swift +177 -0
  122. package/platforms/macos/src/vendor/Lottie/Private/MainThread/LayerContainers/CompLayers/ImageCompositionLayer.swift +54 -0
  123. package/platforms/macos/src/vendor/Lottie/Private/MainThread/LayerContainers/CompLayers/MaskContainerLayer.swift +191 -0
  124. package/platforms/macos/src/vendor/Lottie/Private/MainThread/LayerContainers/CompLayers/NullCompositionLayer.swift +28 -0
  125. package/platforms/macos/src/vendor/Lottie/Private/MainThread/LayerContainers/CompLayers/PreCompositionLayer.swift +137 -0
  126. package/platforms/macos/src/vendor/Lottie/Private/MainThread/LayerContainers/CompLayers/ShapeCompositionLayer.swift +60 -0
  127. package/platforms/macos/src/vendor/Lottie/Private/MainThread/LayerContainers/CompLayers/SolidCompositionLayer.swift +57 -0
  128. package/platforms/macos/src/vendor/Lottie/Private/MainThread/LayerContainers/CompLayers/TextCompositionLayer.swift +247 -0
  129. package/platforms/macos/src/vendor/Lottie/Private/MainThread/LayerContainers/MainThreadAnimationLayer.swift +321 -0
  130. package/platforms/macos/src/vendor/Lottie/Private/MainThread/LayerContainers/Utility/CachedImageProvider.swift +56 -0
  131. package/platforms/macos/src/vendor/Lottie/Private/MainThread/LayerContainers/Utility/CompositionLayersInitializer.swift +103 -0
  132. package/platforms/macos/src/vendor/Lottie/Private/MainThread/LayerContainers/Utility/CoreTextRenderLayer.swift +424 -0
  133. package/platforms/macos/src/vendor/Lottie/Private/MainThread/LayerContainers/Utility/InvertedMatteLayer.swift +55 -0
  134. package/platforms/macos/src/vendor/Lottie/Private/MainThread/LayerContainers/Utility/LayerFontProvider.swift +39 -0
  135. package/platforms/macos/src/vendor/Lottie/Private/MainThread/LayerContainers/Utility/LayerImageProvider.swift +52 -0
  136. package/platforms/macos/src/vendor/Lottie/Private/MainThread/LayerContainers/Utility/LayerTextProvider.swift +38 -0
  137. package/platforms/macos/src/vendor/Lottie/Private/MainThread/LayerContainers/Utility/LayerTransformNode.swift +149 -0
  138. package/platforms/macos/src/vendor/Lottie/Private/MainThread/NodeRenderSystem/Extensions/ItemsExtension.swift +146 -0
  139. package/platforms/macos/src/vendor/Lottie/Private/MainThread/NodeRenderSystem/NodeProperties/NodeProperty.swift +62 -0
  140. package/platforms/macos/src/vendor/Lottie/Private/MainThread/NodeRenderSystem/NodeProperties/Protocols/AnyNodeProperty.swift +53 -0
  141. package/platforms/macos/src/vendor/Lottie/Private/MainThread/NodeRenderSystem/NodeProperties/Protocols/AnyValueContainer.swift +26 -0
  142. package/platforms/macos/src/vendor/Lottie/Private/MainThread/NodeRenderSystem/NodeProperties/Protocols/KeypathSearchable.swift +23 -0
  143. package/platforms/macos/src/vendor/Lottie/Private/MainThread/NodeRenderSystem/NodeProperties/Protocols/NodePropertyMap.swift +43 -0
  144. package/platforms/macos/src/vendor/Lottie/Private/MainThread/NodeRenderSystem/NodeProperties/ValueContainer.swift +47 -0
  145. package/platforms/macos/src/vendor/Lottie/Private/MainThread/NodeRenderSystem/NodeProperties/ValueProviders/GroupInterpolator.swift +39 -0
  146. package/platforms/macos/src/vendor/Lottie/Private/MainThread/NodeRenderSystem/NodeProperties/ValueProviders/SingleValueProvider.swift +43 -0
  147. package/platforms/macos/src/vendor/Lottie/Private/MainThread/NodeRenderSystem/Nodes/LayerEffectNodes/DropShadowNode.swift +102 -0
  148. package/platforms/macos/src/vendor/Lottie/Private/MainThread/NodeRenderSystem/Nodes/LayerEffectNodes/LayerEffectNode.swift +25 -0
  149. package/platforms/macos/src/vendor/Lottie/Private/MainThread/NodeRenderSystem/Nodes/ModifierNodes/RoundedCornersNode.swift +85 -0
  150. package/platforms/macos/src/vendor/Lottie/Private/MainThread/NodeRenderSystem/Nodes/ModifierNodes/TrimPathNode.swift +281 -0
  151. package/platforms/macos/src/vendor/Lottie/Private/MainThread/NodeRenderSystem/Nodes/OutputNodes/GroupOutputNode.swift +74 -0
  152. package/platforms/macos/src/vendor/Lottie/Private/MainThread/NodeRenderSystem/Nodes/OutputNodes/PassThroughOutputNode.swift +47 -0
  153. package/platforms/macos/src/vendor/Lottie/Private/MainThread/NodeRenderSystem/Nodes/OutputNodes/PathOutputNode.swift +89 -0
  154. package/platforms/macos/src/vendor/Lottie/Private/MainThread/NodeRenderSystem/Nodes/OutputNodes/Renderables/FillRenderer.swift +69 -0
  155. package/platforms/macos/src/vendor/Lottie/Private/MainThread/NodeRenderSystem/Nodes/OutputNodes/Renderables/GradientFillRenderer.swift +253 -0
  156. package/platforms/macos/src/vendor/Lottie/Private/MainThread/NodeRenderSystem/Nodes/OutputNodes/Renderables/GradientStrokeRenderer.swift +64 -0
  157. package/platforms/macos/src/vendor/Lottie/Private/MainThread/NodeRenderSystem/Nodes/OutputNodes/Renderables/LegacyGradientFillRenderer.swift +158 -0
  158. package/platforms/macos/src/vendor/Lottie/Private/MainThread/NodeRenderSystem/Nodes/OutputNodes/Renderables/StrokeRenderer.swift +165 -0
  159. package/platforms/macos/src/vendor/Lottie/Private/MainThread/NodeRenderSystem/Nodes/PathNodes/EllipseNode.swift +143 -0
  160. package/platforms/macos/src/vendor/Lottie/Private/MainThread/NodeRenderSystem/Nodes/PathNodes/PolygonNode.swift +173 -0
  161. package/platforms/macos/src/vendor/Lottie/Private/MainThread/NodeRenderSystem/Nodes/PathNodes/RectNode.swift +238 -0
  162. package/platforms/macos/src/vendor/Lottie/Private/MainThread/NodeRenderSystem/Nodes/PathNodes/ShapeNode.swift +73 -0
  163. package/platforms/macos/src/vendor/Lottie/Private/MainThread/NodeRenderSystem/Nodes/PathNodes/StarNode.swift +224 -0
  164. package/platforms/macos/src/vendor/Lottie/Private/MainThread/NodeRenderSystem/Nodes/RenderContainers/GroupNode.swift +162 -0
  165. package/platforms/macos/src/vendor/Lottie/Private/MainThread/NodeRenderSystem/Nodes/RenderNodes/FillNode.swift +88 -0
  166. package/platforms/macos/src/vendor/Lottie/Private/MainThread/NodeRenderSystem/Nodes/RenderNodes/GradientFillNode.swift +103 -0
  167. package/platforms/macos/src/vendor/Lottie/Private/MainThread/NodeRenderSystem/Nodes/RenderNodes/GradientStrokeNode.swift +149 -0
  168. package/platforms/macos/src/vendor/Lottie/Private/MainThread/NodeRenderSystem/Nodes/RenderNodes/StrokeNode.swift +179 -0
  169. package/platforms/macos/src/vendor/Lottie/Private/MainThread/NodeRenderSystem/Nodes/Text/TextAnimatorNode.swift +345 -0
  170. package/platforms/macos/src/vendor/Lottie/Private/MainThread/NodeRenderSystem/Protocols/AnimatorNode.swift +197 -0
  171. package/platforms/macos/src/vendor/Lottie/Private/MainThread/NodeRenderSystem/Protocols/PathNode.swift +20 -0
  172. package/platforms/macos/src/vendor/Lottie/Private/MainThread/NodeRenderSystem/Protocols/RenderNode.swift +60 -0
  173. package/platforms/macos/src/vendor/Lottie/Private/MainThread/NodeRenderSystem/RenderLayers/ShapeContainerLayer.swift +78 -0
  174. package/platforms/macos/src/vendor/Lottie/Private/MainThread/NodeRenderSystem/RenderLayers/ShapeRenderLayer.swift +98 -0
  175. package/platforms/macos/src/vendor/Lottie/Private/Model/Assets/Asset.swift +51 -0
  176. package/platforms/macos/src/vendor/Lottie/Private/Model/Assets/AssetLibrary.swift +75 -0
  177. package/platforms/macos/src/vendor/Lottie/Private/Model/Assets/ImageAsset.swift +141 -0
  178. package/platforms/macos/src/vendor/Lottie/Private/Model/Assets/PrecompAsset.swift +47 -0
  179. package/platforms/macos/src/vendor/Lottie/Private/Model/DictionaryInitializable.swift +73 -0
  180. package/platforms/macos/src/vendor/Lottie/Private/Model/DotLottie/DotLottieAnimation.swift +57 -0
  181. package/platforms/macos/src/vendor/Lottie/Private/Model/DotLottie/DotLottieImageProvider.swift +89 -0
  182. package/platforms/macos/src/vendor/Lottie/Private/Model/DotLottie/DotLottieManifest.swift +42 -0
  183. package/platforms/macos/src/vendor/Lottie/Private/Model/DotLottie/DotLottieUtils.swift +71 -0
  184. package/platforms/macos/src/vendor/Lottie/Private/Model/Extensions/Bundle.swift +58 -0
  185. package/platforms/macos/src/vendor/Lottie/Private/Model/Extensions/KeyedDecodingContainerExtensions.swift +71 -0
  186. package/platforms/macos/src/vendor/Lottie/Private/Model/Keyframes/KeyframeData.swift +111 -0
  187. package/platforms/macos/src/vendor/Lottie/Private/Model/Keyframes/KeyframeGroup.swift +265 -0
  188. package/platforms/macos/src/vendor/Lottie/Private/Model/LayerEffects/DropShadowEffect.swift +52 -0
  189. package/platforms/macos/src/vendor/Lottie/Private/Model/LayerEffects/EffectValues/ColorEffectValue.swift +45 -0
  190. package/platforms/macos/src/vendor/Lottie/Private/Model/LayerEffects/EffectValues/EffectValue.swift +98 -0
  191. package/platforms/macos/src/vendor/Lottie/Private/Model/LayerEffects/EffectValues/Vector1DEffectValue.swift +45 -0
  192. package/platforms/macos/src/vendor/Lottie/Private/Model/LayerEffects/LayerEffect.swift +103 -0
  193. package/platforms/macos/src/vendor/Lottie/Private/Model/LayerStyles/DropShadowStyle.swift +79 -0
  194. package/platforms/macos/src/vendor/Lottie/Private/Model/LayerStyles/LayerStyle.swift +85 -0
  195. package/platforms/macos/src/vendor/Lottie/Private/Model/Layers/ImageLayerModel.swift +49 -0
  196. package/platforms/macos/src/vendor/Lottie/Private/Model/Layers/LayerModel.swift +266 -0
  197. package/platforms/macos/src/vendor/Lottie/Private/Model/Layers/PreCompLayerModel.swift +74 -0
  198. package/platforms/macos/src/vendor/Lottie/Private/Model/Layers/ShapeLayerModel.swift +50 -0
  199. package/platforms/macos/src/vendor/Lottie/Private/Model/Layers/SolidLayerModel.swift +63 -0
  200. package/platforms/macos/src/vendor/Lottie/Private/Model/Layers/TextLayerModel.swift +65 -0
  201. package/platforms/macos/src/vendor/Lottie/Private/Model/Objects/DashPattern.swift +42 -0
  202. package/platforms/macos/src/vendor/Lottie/Private/Model/Objects/Marker.swift +35 -0
  203. package/platforms/macos/src/vendor/Lottie/Private/Model/Objects/Mask.swift +80 -0
  204. package/platforms/macos/src/vendor/Lottie/Private/Model/Objects/Transform.swift +261 -0
  205. package/platforms/macos/src/vendor/Lottie/Private/Model/ShapeItems/Ellipse.swift +79 -0
  206. package/platforms/macos/src/vendor/Lottie/Private/Model/ShapeItems/Fill.swift +79 -0
  207. package/platforms/macos/src/vendor/Lottie/Private/Model/ShapeItems/GradientFill.swift +142 -0
  208. package/platforms/macos/src/vendor/Lottie/Private/Model/ShapeItems/GradientStroke.swift +244 -0
  209. package/platforms/macos/src/vendor/Lottie/Private/Model/ShapeItems/Group.swift +55 -0
  210. package/platforms/macos/src/vendor/Lottie/Private/Model/ShapeItems/Merge.swift +63 -0
  211. package/platforms/macos/src/vendor/Lottie/Private/Model/ShapeItems/Rectangle.swift +79 -0
  212. package/platforms/macos/src/vendor/Lottie/Private/Model/ShapeItems/Repeater.swift +181 -0
  213. package/platforms/macos/src/vendor/Lottie/Private/Model/ShapeItems/RoundedCorners.swift +53 -0
  214. package/platforms/macos/src/vendor/Lottie/Private/Model/ShapeItems/Shape.swift +63 -0
  215. package/platforms/macos/src/vendor/Lottie/Private/Model/ShapeItems/ShapeItem.swift +171 -0
  216. package/platforms/macos/src/vendor/Lottie/Private/Model/ShapeItems/ShapeTransform.swift +202 -0
  217. package/platforms/macos/src/vendor/Lottie/Private/Model/ShapeItems/Star.swift +136 -0
  218. package/platforms/macos/src/vendor/Lottie/Private/Model/ShapeItems/Stroke.swift +144 -0
  219. package/platforms/macos/src/vendor/Lottie/Private/Model/ShapeItems/Trim.swift +90 -0
  220. package/platforms/macos/src/vendor/Lottie/Private/Model/Text/Font.swift +67 -0
  221. package/platforms/macos/src/vendor/Lottie/Private/Model/Text/Glyph.swift +94 -0
  222. package/platforms/macos/src/vendor/Lottie/Private/Model/Text/TextAnimator.swift +240 -0
  223. package/platforms/macos/src/vendor/Lottie/Private/Model/Text/TextDocument.swift +142 -0
  224. package/platforms/macos/src/vendor/Lottie/Private/RootAnimationLayer.swift +54 -0
  225. package/platforms/macos/src/vendor/Lottie/Private/Utility/Debugging/AnimatorNodeDebugging.swift +23 -0
  226. package/platforms/macos/src/vendor/Lottie/Private/Utility/Debugging/LayerDebugging.swift +231 -0
  227. package/platforms/macos/src/vendor/Lottie/Private/Utility/Debugging/TestHelpers.swift +10 -0
  228. package/platforms/macos/src/vendor/Lottie/Private/Utility/Extensions/AnimationKeypathExtension.swift +303 -0
  229. package/platforms/macos/src/vendor/Lottie/Private/Utility/Extensions/BlendMode+Filter.swift +31 -0
  230. package/platforms/macos/src/vendor/Lottie/Private/Utility/Extensions/CGColor+RGB.swift +45 -0
  231. package/platforms/macos/src/vendor/Lottie/Private/Utility/Extensions/CGFloatExtensions.swift +152 -0
  232. package/platforms/macos/src/vendor/Lottie/Private/Utility/Extensions/DataExtension.swift +33 -0
  233. package/platforms/macos/src/vendor/Lottie/Private/Utility/Extensions/MathKit.swift +454 -0
  234. package/platforms/macos/src/vendor/Lottie/Private/Utility/Extensions/StringExtensions.swift +44 -0
  235. package/platforms/macos/src/vendor/Lottie/Private/Utility/Helpers/AnimationContext.swift +95 -0
  236. package/platforms/macos/src/vendor/Lottie/Private/Utility/Helpers/AnyEquatable.swift +23 -0
  237. package/platforms/macos/src/vendor/Lottie/Private/Utility/Helpers/Binding+Map.swift +19 -0
  238. package/platforms/macos/src/vendor/Lottie/Private/Utility/Helpers/View+ValueChanged.swift +35 -0
  239. package/platforms/macos/src/vendor/Lottie/Private/Utility/Interpolatable/InterpolatableExtensions.swift +143 -0
  240. package/platforms/macos/src/vendor/Lottie/Private/Utility/Interpolatable/KeyframeExtensions.swift +47 -0
  241. package/platforms/macos/src/vendor/Lottie/Private/Utility/Interpolatable/KeyframeInterpolator.swift +250 -0
  242. package/platforms/macos/src/vendor/Lottie/Private/Utility/LottieAnimationSource.swift +51 -0
  243. package/platforms/macos/src/vendor/Lottie/Private/Utility/Primitives/BezierPath.swift +509 -0
  244. package/platforms/macos/src/vendor/Lottie/Private/Utility/Primitives/BezierPathRoundExtension.swift +163 -0
  245. package/platforms/macos/src/vendor/Lottie/Private/Utility/Primitives/CGPointExtension.swift +35 -0
  246. package/platforms/macos/src/vendor/Lottie/Private/Utility/Primitives/ColorExtension.swift +101 -0
  247. package/platforms/macos/src/vendor/Lottie/Private/Utility/Primitives/CompoundBezierPath.swift +169 -0
  248. package/platforms/macos/src/vendor/Lottie/Private/Utility/Primitives/CurveVertex.swift +190 -0
  249. package/platforms/macos/src/vendor/Lottie/Private/Utility/Primitives/PathElement.swift +78 -0
  250. package/platforms/macos/src/vendor/Lottie/Private/Utility/Primitives/UnitBezier.swift +119 -0
  251. package/platforms/macos/src/vendor/Lottie/Private/Utility/Primitives/VectorsExtensions.swift +368 -0
  252. package/platforms/macos/src/vendor/Lottie/Public/Animation/LottieAnimation.swift +179 -0
  253. package/platforms/macos/src/vendor/Lottie/Public/Animation/LottieAnimationHelpers.swift +317 -0
  254. package/platforms/macos/src/vendor/Lottie/Public/Animation/LottieAnimationLayer.swift +1582 -0
  255. package/platforms/macos/src/vendor/Lottie/Public/Animation/LottieAnimationView.swift +1090 -0
  256. package/platforms/macos/src/vendor/Lottie/Public/Animation/LottieAnimationViewInitializers.swift +226 -0
  257. package/platforms/macos/src/vendor/Lottie/Public/Animation/LottiePlaybackMode.swift +266 -0
  258. package/platforms/macos/src/vendor/Lottie/Public/Animation/LottieView.swift +643 -0
  259. package/platforms/macos/src/vendor/Lottie/Public/AnimationCache/AnimationCacheProvider.swift +21 -0
  260. package/platforms/macos/src/vendor/Lottie/Public/AnimationCache/DefaultAnimationCache.swift +67 -0
  261. package/platforms/macos/src/vendor/Lottie/Public/AnimationCache/LRUAnimationCache.swift +11 -0
  262. package/platforms/macos/src/vendor/Lottie/Public/AnimationCache/LottieAnimationCache.swift +15 -0
  263. package/platforms/macos/src/vendor/Lottie/Public/Configuration/DecodingStrategy.swift +15 -0
  264. package/platforms/macos/src/vendor/Lottie/Public/Configuration/LottieConfiguration.swift +54 -0
  265. package/platforms/macos/src/vendor/Lottie/Public/Configuration/LottieURLSession.swift +31 -0
  266. package/platforms/macos/src/vendor/Lottie/Public/Configuration/ReducedMotionOption.swift +128 -0
  267. package/platforms/macos/src/vendor/Lottie/Public/Configuration/RenderingEngineOption.swift +117 -0
  268. package/platforms/macos/src/vendor/Lottie/Public/Controls/AnimatedButton.swift +128 -0
  269. package/platforms/macos/src/vendor/Lottie/Public/Controls/AnimatedControl.swift +248 -0
  270. package/platforms/macos/src/vendor/Lottie/Public/Controls/AnimatedSwitch.swift +271 -0
  271. package/platforms/macos/src/vendor/Lottie/Public/Controls/LottieButton.swift +119 -0
  272. package/platforms/macos/src/vendor/Lottie/Public/Controls/LottieSwitch.swift +144 -0
  273. package/platforms/macos/src/vendor/Lottie/Public/Controls/LottieViewType.swift +79 -0
  274. package/platforms/macos/src/vendor/Lottie/Public/DotLottie/Cache/DotLottieCache.swift +66 -0
  275. package/platforms/macos/src/vendor/Lottie/Public/DotLottie/Cache/DotLottieCacheProvider.swift +21 -0
  276. package/platforms/macos/src/vendor/Lottie/Public/DotLottie/DotLottieConfiguration.swift +72 -0
  277. package/platforms/macos/src/vendor/Lottie/Public/DotLottie/DotLottieFile.swift +186 -0
  278. package/platforms/macos/src/vendor/Lottie/Public/DotLottie/DotLottieFileHelpers.swift +386 -0
  279. package/platforms/macos/src/vendor/Lottie/Public/DynamicProperties/AnimationKeypath.swift +57 -0
  280. package/platforms/macos/src/vendor/Lottie/Public/DynamicProperties/AnyValueProvider.swift +132 -0
  281. package/platforms/macos/src/vendor/Lottie/Public/DynamicProperties/ValueProviders/ColorValueProvider.swift +96 -0
  282. package/platforms/macos/src/vendor/Lottie/Public/DynamicProperties/ValueProviders/FloatValueProvider.swift +81 -0
  283. package/platforms/macos/src/vendor/Lottie/Public/DynamicProperties/ValueProviders/GradientValueProvider.swift +138 -0
  284. package/platforms/macos/src/vendor/Lottie/Public/DynamicProperties/ValueProviders/PointValueProvider.swift +81 -0
  285. package/platforms/macos/src/vendor/Lottie/Public/DynamicProperties/ValueProviders/SizeValueProvider.swift +81 -0
  286. package/platforms/macos/src/vendor/Lottie/Public/FontProvider/AnimationFontProvider.swift +41 -0
  287. package/platforms/macos/src/vendor/Lottie/Public/ImageProvider/AnimationImageProvider.swift +43 -0
  288. package/platforms/macos/src/vendor/Lottie/Public/Keyframes/Interpolatable.swift +285 -0
  289. package/platforms/macos/src/vendor/Lottie/Public/Keyframes/Keyframe.swift +98 -0
  290. package/platforms/macos/src/vendor/Lottie/Public/Logging/LottieLogger.swift +138 -0
  291. package/platforms/macos/src/vendor/Lottie/Public/Primitives/AnimationTime.swift +15 -0
  292. package/platforms/macos/src/vendor/Lottie/Public/Primitives/LottieColor.swift +43 -0
  293. package/platforms/macos/src/vendor/Lottie/Public/Primitives/Vectors.swift +36 -0
  294. package/platforms/macos/src/vendor/Lottie/Public/TextProvider/AnimationTextProvider.swift +135 -0
  295. package/platforms/macos/src/vendor/Lottie/Public/iOS/AnimationSubview.swift +19 -0
  296. package/platforms/macos/src/vendor/Lottie/Public/iOS/BundleImageProvider.swift +98 -0
  297. package/platforms/macos/src/vendor/Lottie/Public/iOS/Compatibility/CompatibleAnimationKeypath.swift +33 -0
  298. package/platforms/macos/src/vendor/Lottie/Public/iOS/Compatibility/CompatibleAnimationView.swift +567 -0
  299. package/platforms/macos/src/vendor/Lottie/Public/iOS/Compatibility/CompatibleFontProvider.swift +41 -0
  300. package/platforms/macos/src/vendor/Lottie/Public/iOS/Compatibility/CompatibleImageProvider.swift +43 -0
  301. package/platforms/macos/src/vendor/Lottie/Public/iOS/FilepathImageProvider.swift +78 -0
  302. package/platforms/macos/src/vendor/Lottie/Public/iOS/LottieAnimationViewBase.swift +85 -0
  303. package/platforms/macos/src/vendor/Lottie/Public/iOS/UIColorExtension.swift +23 -0
  304. package/platforms/macos/src/vendor/Lottie/Public/macOS/AnimationSubview.macOS.swift +19 -0
  305. package/platforms/macos/src/vendor/Lottie/Public/macOS/BundleImageProvider.macOS.swift +90 -0
  306. package/platforms/macos/src/vendor/Lottie/Public/macOS/FilepathImageProvider.macOS.swift +86 -0
  307. package/platforms/macos/src/vendor/Lottie/Public/macOS/LottieAnimationViewBase.macOS.swift +102 -0
  308. package/src/Lottie.macos.tsrx +129 -0
  309. package/src/Lottie.tsrx +3 -2
  310. package/src/Lottie.web.tsrx +28 -11
  311. package/src/index.macos.ts +2 -0
  312. package/src/props.ts +6 -1
  313. package/types/generated/props.d.ts +5 -1
@@ -0,0 +1,1582 @@
1
+ //
2
+ // LottieAnimationLayer.swift
3
+ // Lottie
4
+ //
5
+
6
+ import QuartzCore
7
+
8
+ // MARK: - LottieAnimationLayer
9
+
10
+ /// A CALayer subclass for rendering Lottie animations.
11
+ /// - Also available as a SwiftUI view (`LottieView`) and a UIView subclass (`LottieAnimationView`)
12
+ public class LottieAnimationLayer: CALayer {
13
+
14
+ // MARK: Lifecycle
15
+
16
+ /// Initializes a LottieAnimationLayer with an animation.
17
+ public init(
18
+ animation: LottieAnimation?,
19
+ imageProvider: AnimationImageProvider? = nil,
20
+ textProvider: AnimationKeypathTextProvider = DefaultTextProvider(),
21
+ fontProvider: AnimationFontProvider = DefaultFontProvider(),
22
+ configuration: LottieConfiguration = .shared,
23
+ logger: LottieLogger = .shared
24
+ ) {
25
+ self.animation = animation
26
+ self.imageProvider = imageProvider ?? BundleImageProvider(bundle: Bundle.main, searchPath: nil)
27
+ self.textProvider = textProvider
28
+ self.fontProvider = fontProvider
29
+ self.configuration = configuration
30
+ screenScale = 1
31
+ self.logger = logger
32
+ super.init()
33
+ makeAnimationLayer(usingEngine: configuration.renderingEngine)
34
+ if let animation {
35
+ frame = animation.bounds
36
+ }
37
+ }
38
+
39
+ /// Initializes an LottieAnimationLayer with a .lottie file.
40
+ public init(
41
+ dotLottie: DotLottieFile?,
42
+ animationId: String? = nil,
43
+ textProvider: AnimationKeypathTextProvider = DefaultTextProvider(),
44
+ fontProvider: AnimationFontProvider = DefaultFontProvider(),
45
+ configuration: LottieConfiguration = .shared,
46
+ logger: LottieLogger = .shared
47
+ ) {
48
+ let dotLottieAnimation = dotLottie?.animation(for: animationId)
49
+ animation = dotLottieAnimation?.animation
50
+ imageProvider = dotLottie?.imageProvider ?? BundleImageProvider(bundle: Bundle.main, searchPath: nil)
51
+ self.textProvider = textProvider
52
+ self.fontProvider = fontProvider
53
+ self.configuration = configuration
54
+ screenScale = 1
55
+ self.logger = logger
56
+ super.init()
57
+ loopMode = dotLottieAnimation?.configuration.loopMode ?? .playOnce
58
+ animationSpeed = CGFloat(dotLottieAnimation?.configuration.speed ?? 1)
59
+ makeAnimationLayer(usingEngine: configuration.renderingEngine)
60
+ if let animation {
61
+ frame = animation.bounds
62
+ }
63
+ }
64
+
65
+ public init(
66
+ configuration: LottieConfiguration = .shared,
67
+ logger: LottieLogger = .shared
68
+ ) {
69
+ animation = nil
70
+ imageProvider = BundleImageProvider(bundle: Bundle.main, searchPath: nil)
71
+ textProvider = DefaultTextProvider()
72
+ fontProvider = DefaultFontProvider()
73
+ self.configuration = configuration
74
+ screenScale = 1
75
+ self.logger = logger
76
+ super.init()
77
+ }
78
+
79
+ /// Called by CoreAnimation to create a shadow copy of this layer
80
+ /// More details: https://developer.apple.com/documentation/quartzcore/calayer/1410842-init
81
+ override init(layer: Any) {
82
+ guard let typedLayer = layer as? Self else {
83
+ fatalError("\(Self.self).init(layer:) incorrectly called with \(type(of: layer))")
84
+ }
85
+
86
+ animation = typedLayer.animation
87
+ imageProvider = typedLayer.imageProvider
88
+ textProvider = typedLayer.textProvider
89
+ fontProvider = typedLayer.fontProvider
90
+ logger = typedLayer.logger
91
+ screenScale = typedLayer.screenScale
92
+ configuration = typedLayer.configuration
93
+ super.init(layer: typedLayer)
94
+ }
95
+
96
+ required init?(coder _: NSCoder) {
97
+ fatalError("init(coder:) has not been implemented")
98
+ }
99
+
100
+ // MARK: Open
101
+
102
+ /// Plays the animation from its current state to the end.
103
+ ///
104
+ /// - Parameter completion: An optional completion closure to be called when the animation completes playing.
105
+ open func play(completion: LottieCompletionBlock? = nil) {
106
+ guard let animation else { return }
107
+
108
+ defer {
109
+ currentPlaybackMode = .playing(.fromProgress(nil, toProgress: 1, loopMode: loopMode))
110
+ }
111
+
112
+ if overrideReducedMotionIfNeeded(completion: completion) { return }
113
+
114
+ /// Build a context for the animation.
115
+ let context = AnimationContext(
116
+ playFrom: CGFloat(animation.startFrame),
117
+ playTo: CGFloat(animation.endFrame),
118
+ framerate: animation.framerate,
119
+ closure: completion
120
+ )
121
+ removeCurrentAnimationIfNecessary()
122
+ addNewAnimationForContext(context)
123
+ }
124
+
125
+ /// Plays the animation from a progress (0-1) to a progress (0-1).
126
+ ///
127
+ /// - Parameter fromProgress: The start progress of the animation. If `nil` the animation will start at the current progress.
128
+ /// - Parameter toProgress: The end progress of the animation.
129
+ /// - Parameter loopMode: The loop behavior of the animation. If `nil` the layer's `loopMode` property will be used.
130
+ /// - Parameter completion: An optional completion closure to be called when the animation stops.
131
+ open func play(
132
+ fromProgress: AnimationProgressTime? = nil,
133
+ toProgress: AnimationProgressTime,
134
+ loopMode: LottieLoopMode? = nil,
135
+ completion: LottieCompletionBlock? = nil
136
+ ) {
137
+ guard let animation else { return }
138
+
139
+ defer {
140
+ currentPlaybackMode = .playing(.fromProgress(fromProgress, toProgress: toProgress, loopMode: loopMode ?? self.loopMode))
141
+ }
142
+
143
+ if overrideReducedMotionIfNeeded(completion: completion) { return }
144
+
145
+ removeCurrentAnimationIfNecessary()
146
+ if let loopMode {
147
+ /// Set the loop mode, if one was supplied
148
+ self.loopMode = loopMode
149
+ }
150
+ let context = AnimationContext(
151
+ playFrom: animation.frameTime(forProgress: fromProgress ?? currentProgress),
152
+ playTo: animation.frameTime(forProgress: toProgress),
153
+ framerate: animation.framerate,
154
+ closure: completion
155
+ )
156
+ addNewAnimationForContext(context)
157
+ }
158
+
159
+ /// Plays the animation from a start frame to an end frame in the animation's framerate.
160
+ ///
161
+ /// - Parameter fromFrame: The start frame of the animation. If `nil` the animation will start at the current frame.
162
+ /// - Parameter toFrame: The end frame of the animation.
163
+ /// - Parameter loopMode: The loop behavior of the animation. If `nil` the layer's `loopMode` property will be used.
164
+ /// - Parameter completion: An optional completion closure to be called when the animation stops.
165
+ open func play(
166
+ fromFrame: AnimationFrameTime? = nil,
167
+ toFrame: AnimationFrameTime,
168
+ loopMode: LottieLoopMode? = nil,
169
+ completion: LottieCompletionBlock? = nil
170
+ ) {
171
+ guard let animation else { return }
172
+
173
+ defer {
174
+ currentPlaybackMode = .playing(.fromFrame(fromFrame, toFrame: toFrame, loopMode: loopMode ?? self.loopMode))
175
+ }
176
+
177
+ if overrideReducedMotionIfNeeded(completion: completion) { return }
178
+
179
+ removeCurrentAnimationIfNecessary()
180
+ if let loopMode {
181
+ /// Set the loop mode, if one was supplied
182
+ self.loopMode = loopMode
183
+ }
184
+
185
+ let context = AnimationContext(
186
+ playFrom: fromFrame ?? currentFrame,
187
+ playTo: toFrame,
188
+ framerate: animation.framerate,
189
+ closure: completion
190
+ )
191
+ addNewAnimationForContext(context)
192
+ }
193
+
194
+ /// Plays the animation from a named marker to another marker.
195
+ ///
196
+ /// Markers are point in time that are encoded into the Animation data and assigned
197
+ /// a name.
198
+ ///
199
+ /// NOTE: If markers are not found the play command will exit.
200
+ ///
201
+ /// - Parameter fromMarker: The start marker for the animation playback. If `nil` the
202
+ /// animation will start at the current progress.
203
+ /// - Parameter toMarker: The end marker for the animation playback.
204
+ /// - Parameter playEndMarkerFrame: A flag to determine whether or not to play the frame of the end marker. If the
205
+ /// end marker represents the end of the section to play, it should be to true. If the provided end marker
206
+ /// represents the beginning of the next section, it should be false.
207
+ /// - Parameter loopMode: The loop behavior of the animation. If `nil` the layer's `loopMode` property will be used.
208
+ /// - Parameter completion: An optional completion closure to be called when the animation stops.
209
+ open func play(
210
+ fromMarker: String? = nil,
211
+ toMarker: String,
212
+ playEndMarkerFrame: Bool = true,
213
+ loopMode: LottieLoopMode? = nil,
214
+ completion: LottieCompletionBlock? = nil
215
+ ) {
216
+ defer {
217
+ currentPlaybackMode = .playing(.fromMarker(
218
+ fromMarker,
219
+ toMarker: toMarker,
220
+ playEndMarkerFrame: playEndMarkerFrame,
221
+ loopMode: loopMode ?? self.loopMode
222
+ ))
223
+ }
224
+
225
+ if overrideReducedMotionIfNeeded(completion: completion) { return }
226
+
227
+ guard let animation, let markers = animation.markerMap, let to = markers[toMarker] else {
228
+ return
229
+ }
230
+
231
+ removeCurrentAnimationIfNecessary()
232
+ if let loopMode {
233
+ /// Set the loop mode, if one was supplied
234
+ self.loopMode = loopMode
235
+ }
236
+
237
+ let fromTime: CGFloat =
238
+ if let fromName = fromMarker, let from = markers[fromName] {
239
+ CGFloat(from.frameTime)
240
+ } else {
241
+ currentFrame
242
+ }
243
+
244
+ let playTo = playEndMarkerFrame ? CGFloat(to.frameTime) : CGFloat(to.frameTime) - 1
245
+ let context = AnimationContext(
246
+ playFrom: fromTime,
247
+ playTo: playTo,
248
+ framerate: animation.framerate,
249
+ closure: completion
250
+ )
251
+ addNewAnimationForContext(context)
252
+ }
253
+
254
+ /// Plays the animation from a named marker to the end of the marker's duration.
255
+ ///
256
+ /// A marker is a point in time with an associated duration that is encoded into the
257
+ /// animation data and assigned a name.
258
+ ///
259
+ /// NOTE: If marker is not found the play command will exit.
260
+ ///
261
+ /// - Parameter marker: The start marker for the animation playback.
262
+ /// - Parameter loopMode: The loop behavior of the animation. If `nil` the layer's `loopMode` property will be used.
263
+ /// - Parameter completion: An optional completion closure to be called when the animation stops.
264
+ open func play(
265
+ marker: String,
266
+ loopMode: LottieLoopMode? = nil,
267
+ completion: LottieCompletionBlock? = nil
268
+ ) {
269
+ guard let from = animation?.markerMap?[marker] else {
270
+ return
271
+ }
272
+
273
+ defer {
274
+ currentPlaybackMode = .playing(.marker(marker, loopMode: loopMode ?? self.loopMode))
275
+ }
276
+
277
+ if overrideReducedMotionIfNeeded(completion: completion) { return }
278
+
279
+ play(
280
+ fromFrame: from.frameTime,
281
+ toFrame: from.frameTime + from.durationFrameTime,
282
+ loopMode: loopMode,
283
+ completion: completion
284
+ )
285
+ }
286
+
287
+ /// Plays the given markers sequentially in order.
288
+ ///
289
+ /// A marker is a point in time with an associated duration that is encoded into the
290
+ /// animation data and assigned a name. Multiple markers can be played sequentially
291
+ /// to create programmable animations.
292
+ ///
293
+ /// If a marker is not found, it will be skipped.
294
+ ///
295
+ /// If a marker doesn't have a duration value, it will play with a duration of 0
296
+ /// (effectively being skipped).
297
+ ///
298
+ /// If another animation is played (by calling any `play` method) while this
299
+ /// marker sequence is playing, the marker sequence will be cancelled.
300
+ ///
301
+ /// - Parameter markers: The list of markers to play sequentially.
302
+ /// - Parameter completion: An optional completion closure to be called when the animation stops.
303
+ open func play(
304
+ markers: [String],
305
+ completion: LottieCompletionBlock? = nil
306
+ ) {
307
+ guard !markers.isEmpty else { return }
308
+
309
+ defer {
310
+ currentPlaybackMode = .playing(.markers(markers))
311
+ }
312
+
313
+ if overrideReducedMotionIfNeeded(completion: completion) { return }
314
+
315
+ let markerToPlay = markers[0]
316
+ let followingMarkers = Array(markers.dropFirst())
317
+
318
+ guard animation?.markerMap?[markerToPlay] != nil else {
319
+ play(markers: followingMarkers, completion: completion)
320
+ return
321
+ }
322
+
323
+ play(marker: markerToPlay, loopMode: .playOnce, completion: { [weak self] completed in
324
+ // If the completion handler is called with `completed: false` (which typically means
325
+ // that another animation was played by calling some `play` method),
326
+ // we should cancel the marker sequence and not play the next marker.
327
+ guard completed, let self else {
328
+ completion?(false)
329
+ return
330
+ }
331
+
332
+ if followingMarkers.isEmpty {
333
+ // If we don't have any more markers to play, then the marker sequence has completed.
334
+ completion?(completed)
335
+ } else {
336
+ play(markers: followingMarkers, completion: completion)
337
+ }
338
+ })
339
+ }
340
+
341
+ /// Stops the animation and resets the layer to its start frame.
342
+ ///
343
+ /// The completion closure will be called with `false`
344
+ open func stop() {
345
+ removeCurrentAnimation()
346
+ currentFrame = 0
347
+ }
348
+
349
+ /// Pauses the animation in its current state.
350
+ ///
351
+ /// The completion closure will be called with `false`
352
+ open func pause() {
353
+ pause(at: .currentFrame)
354
+ }
355
+
356
+ /// Pauses the animation at a given state.
357
+ open func pause(at state: LottiePlaybackMode.PausedState) {
358
+ switch state {
359
+ case .currentFrame:
360
+ removeCurrentAnimation()
361
+
362
+ case .progress(let animationProgressTime):
363
+ currentProgress = animationProgressTime
364
+
365
+ case .frame(let animationFrameTime):
366
+ currentFrame = animationFrameTime
367
+
368
+ case .time(let timeInterval):
369
+ currentTime = timeInterval
370
+
371
+ case .marker(let name, let position):
372
+ guard let from = animation?.markerMap?[name] else {
373
+ return
374
+ }
375
+
376
+ switch position {
377
+ case .start:
378
+ currentFrame = from.frameTime
379
+ case .end:
380
+ currentFrame = from.frameTime + from.durationFrameTime
381
+ }
382
+ }
383
+
384
+ currentPlaybackMode = .paused(at: state)
385
+ }
386
+
387
+ @available(*, deprecated, renamed: "setPlaybackMode(_:completion:)", message: "Will be removed in a future major release.")
388
+ open func play(
389
+ _ playbackMode: LottiePlaybackMode,
390
+ animationCompletionHandler: LottieCompletionBlock? = nil
391
+ ) {
392
+ setPlaybackMode(playbackMode, completion: animationCompletionHandler)
393
+ }
394
+
395
+ /// Applies the given `LottiePlaybackMode` to this layer.
396
+ /// - Parameter playbackMode: The playback mode to apply
397
+ /// - Parameter completion: A closure that is called after
398
+ /// an animation triggered by this method completes.
399
+ open func setPlaybackMode(
400
+ _ playbackMode: LottiePlaybackMode,
401
+ completion: LottieCompletionBlock? = nil
402
+ ) {
403
+ switch playbackMode {
404
+ case .paused(at: let state):
405
+ pause(at: state)
406
+
407
+ case .playing(let mode):
408
+ play(mode, completion: completion)
409
+
410
+ case .progress(let progress):
411
+ pause(at: .progress(progress))
412
+
413
+ case .frame(let frame):
414
+ pause(at: .frame(frame))
415
+
416
+ case .time(let time):
417
+ pause(at: .time(time))
418
+
419
+ case .pause:
420
+ pause(at: .currentFrame)
421
+
422
+ case .fromProgress(let from, let to, let loopMode):
423
+ play(.fromProgress(from, toProgress: to, loopMode: loopMode), completion: completion)
424
+
425
+ case .fromFrame(let from, let to, let loopMode):
426
+ play(.fromFrame(from, toFrame: to, loopMode: loopMode), completion: completion)
427
+
428
+ case .fromMarker(let from, let to, let playEndMarkerFrame, let loopMode):
429
+ play(.fromMarker(from, toMarker: to, playEndMarkerFrame: playEndMarkerFrame, loopMode: loopMode), completion: completion)
430
+
431
+ case .marker(let name, let loopMode):
432
+ play(.marker(name, loopMode: loopMode), completion: completion)
433
+
434
+ case .markers(let names):
435
+ play(.markers(names), completion: completion)
436
+ }
437
+ }
438
+
439
+ /// Applies the given `LottiePlaybackMode` to this layer.
440
+ /// - Parameter playbackMode: The playback mode to apply
441
+ /// - Parameter completion: A closure that is called after
442
+ /// an animation triggered by this method completes.
443
+ open func play(_ playbackMode: LottiePlaybackMode.PlaybackMode, completion: LottieCompletionBlock? = nil) {
444
+ switch playbackMode {
445
+ case .fromProgress(let from, let to, let loopMode):
446
+ play(
447
+ fromProgress: from,
448
+ toProgress: to,
449
+ loopMode: loopMode,
450
+ completion: completion
451
+ )
452
+
453
+ case .fromFrame(let from, let to, let loopMode):
454
+ play(
455
+ fromFrame: from,
456
+ toFrame: to,
457
+ loopMode: loopMode,
458
+ completion: completion
459
+ )
460
+
461
+ case .fromMarker(let from, let to, let playEndMarkerFrame, let loopMode):
462
+ play(
463
+ fromMarker: from,
464
+ toMarker: to,
465
+ playEndMarkerFrame: playEndMarkerFrame,
466
+ loopMode: loopMode,
467
+ completion: completion
468
+ )
469
+
470
+ case .marker(let name, loopMode: let loopMode):
471
+ play(marker: name, loopMode: loopMode, completion: completion)
472
+
473
+ case .markers(let names):
474
+ play(markers: names, completion: completion)
475
+ }
476
+ }
477
+
478
+ // MARK: Public
479
+
480
+ /// The current `LottiePlaybackMode` that is being used
481
+ public private(set) var currentPlaybackMode: LottiePlaybackMode?
482
+
483
+ /// Value Providers that have been registered using `setValueProvider(_:keypath:)`
484
+ public private(set) var valueProviders = [AnimationKeypath: AnyValueProvider]()
485
+
486
+ /// A closure called when the animation layer has been loaded.
487
+ /// Will inform the receiver the type of rendering engine that is used for the layer.
488
+ public var animationLayerDidLoad: ((_ animationLayer: LottieAnimationLayer, _ renderingEngine: RenderingEngineOption) -> Void)?
489
+
490
+ /// The configuration that this `LottieAnimationView` uses when playing its animation
491
+ public var configuration: LottieConfiguration {
492
+ didSet {
493
+ if configuration.renderingEngine != oldValue.renderingEngine {
494
+ makeAnimationLayer(usingEngine: configuration.renderingEngine)
495
+ }
496
+ }
497
+ }
498
+
499
+ /// The underlying CALayer created to display the content.
500
+ /// Use this property to change CALayer props like the content's transform, anchor point, etc.
501
+ public var animationLayer: CALayer? {
502
+ rootAnimationLayer
503
+ }
504
+
505
+ public var screenScale: CGFloat {
506
+ didSet {
507
+ rootAnimationLayer?.renderScale = screenScale
508
+ }
509
+ }
510
+
511
+ /// Describes the behavior of an AnimationView when the app is moved to the background.
512
+ ///
513
+ /// The default for the Main Thread animation engine is `pause`,
514
+ /// which pauses the animation when the application moves to
515
+ /// the background. This prevents the animation from consuming CPU
516
+ /// resources when not on-screen. The completion block is called with
517
+ /// `false` for completed.
518
+ ///
519
+ /// The default for the Core Animation engine is `continuePlaying`,
520
+ /// since the Core Animation engine does not have any CPU overhead.
521
+ public var backgroundBehavior: LottieBackgroundBehavior {
522
+ get {
523
+ let currentBackgroundBehavior = _backgroundBehavior ?? .default(for: currentRenderingEngine ?? .mainThread)
524
+
525
+ if
526
+ currentRenderingEngine == .mainThread,
527
+ _backgroundBehavior == .continuePlaying
528
+ {
529
+ logger.assertionFailure("""
530
+ `LottieBackgroundBehavior.continuePlaying` should not be used with the Main Thread
531
+ rendering engine, since this would waste CPU resources on playing an animation
532
+ that is not visible. Consider using a different background mode, or switching to
533
+ the Core Animation rendering engine (which does not have any CPU overhead).
534
+ """)
535
+ }
536
+
537
+ return currentBackgroundBehavior
538
+ }
539
+ set {
540
+ _backgroundBehavior = newValue
541
+ }
542
+ }
543
+
544
+ /// Sets the animation backing the animation layer. Setting this will clear the
545
+ /// layer's contents, completion blocks and current state. The new animation will
546
+ /// be loaded up and set to the beginning of its timeline.
547
+ public var animation: LottieAnimation? {
548
+ didSet {
549
+ makeAnimationLayer(usingEngine: configuration.renderingEngine)
550
+
551
+ if let animation {
552
+ animationLoaded?(self, animation)
553
+ }
554
+ }
555
+ }
556
+
557
+ /// A closure that is called when `self.animation` is loaded. When setting this closure,
558
+ /// it is called immediately if `self.animation` is non-nil.
559
+ ///
560
+ /// When initializing a `LottieAnimationView`, the animation will either be loaded
561
+ /// synchronously (when loading a `LottieAnimation` from a .json file on disk)
562
+ /// or asynchronously (when loading a `DotLottieFile` from disk, or downloading
563
+ /// an animation from a URL). This closure is called in both cases once the
564
+ /// animation is loaded and applied, so can be a useful way to configure this
565
+ /// `LottieAnimationView` regardless of which initializer was used. For example:
566
+ ///
567
+ /// ```
568
+ /// let animationView: LottieAnimationView
569
+ ///
570
+ /// if loadDotLottieFile {
571
+ /// // Loads the .lottie file asynchronously
572
+ /// animationView = LottieAnimationView(dotLottieName: "animation")
573
+ /// } else {
574
+ /// // Loads the .json file synchronously
575
+ /// animationView = LottieAnimationView(name: "animation")
576
+ /// }
577
+ ///
578
+ /// animationView.animationLoaded = { animationView, animation in
579
+ /// // If using a .lottie file, this is called once the file finishes loading.
580
+ /// // If using a .json file, this is called immediately (since the animation is loaded synchronously).
581
+ /// animationView.play()
582
+ /// }
583
+ /// ```
584
+ public var animationLoaded: ((_ animationLayer: LottieAnimationLayer, _ animation: LottieAnimation) -> Void)? {
585
+ didSet {
586
+ if let animation {
587
+ animationLoaded?(self, animation)
588
+ }
589
+ }
590
+ }
591
+
592
+ /// Sets the image provider for the animation layer. An image provider provides the
593
+ /// animation with its required image data.
594
+ ///
595
+ /// Setting this will cause the animation to reload its image contents.
596
+ public var imageProvider: AnimationImageProvider {
597
+ didSet {
598
+ rootAnimationLayer?.imageProvider = imageProvider.cachedImageProvider
599
+ reloadImages()
600
+ }
601
+ }
602
+
603
+ /// Sets the text provider for animation layer. A text provider provides the
604
+ /// animation with values for text layers
605
+ public var textProvider: AnimationKeypathTextProvider {
606
+ didSet {
607
+ rootAnimationLayer?.textProvider = textProvider
608
+ }
609
+ }
610
+
611
+ /// Sets the text provider for animation layer. A text provider provides the
612
+ /// animation with values for text layers
613
+ public var fontProvider: AnimationFontProvider {
614
+ didSet {
615
+ rootAnimationLayer?.fontProvider = fontProvider
616
+ }
617
+ }
618
+
619
+ /// Whether or not the animation is masked to the bounds. Defaults to true.
620
+ public var maskAnimationToBounds = true {
621
+ didSet {
622
+ animationLayer?.masksToBounds = maskAnimationToBounds
623
+ }
624
+ }
625
+
626
+ /// Returns `true` if the animation is currently playing.
627
+ public var isAnimationPlaying: Bool {
628
+ guard let animationLayer = rootAnimationLayer else {
629
+ return false
630
+ }
631
+
632
+ if let valueFromLayer = animationLayer.isAnimationPlaying {
633
+ return valueFromLayer
634
+ } else {
635
+ return animationLayer.animation(forKey: activeAnimationName) != nil
636
+ }
637
+ }
638
+
639
+ /// Sets the loop behavior for `play` calls. Defaults to `playOnce`
640
+ public var loopMode = LottieLoopMode.playOnce {
641
+ didSet {
642
+ updateInFlightAnimation()
643
+ }
644
+ }
645
+
646
+ /// When `true` the animation layer will rasterize its contents when not animating.
647
+ /// Rasterizing will improve performance of static animations.
648
+ ///
649
+ /// Note: this will not produce crisp results at resolutions above the animations natural resolution.
650
+ ///
651
+ /// Defaults to `false`
652
+ public var shouldRasterizeWhenIdle = false {
653
+ didSet {
654
+ updateRasterizationState()
655
+ }
656
+ }
657
+
658
+ /// Sets the current animation time with a Progress Time
659
+ ///
660
+ /// Note: Setting this will stop the current animation, if any.
661
+ /// Note 2: If `animation` is nil, setting this will fallback to 0
662
+ public var currentProgress: AnimationProgressTime {
663
+ set {
664
+ if let animation {
665
+ currentFrame = animation.frameTime(forProgress: newValue)
666
+ currentPlaybackMode = .paused(at: .progress(newValue))
667
+ } else {
668
+ currentFrame = 0
669
+ }
670
+ }
671
+ get {
672
+ if let animation {
673
+ animation.progressTime(forFrame: currentFrame)
674
+ } else {
675
+ 0
676
+ }
677
+ }
678
+ }
679
+
680
+ /// Sets the current animation time with a time in seconds.
681
+ ///
682
+ /// Note: Setting this will stop the current animation, if any.
683
+ /// Note 2: If `animation` is nil, setting this will fallback to 0
684
+ public var currentTime: TimeInterval {
685
+ set {
686
+ if let animation {
687
+ currentFrame = animation.frameTime(forTime: newValue)
688
+ currentPlaybackMode = .paused(at: .time(newValue))
689
+ } else {
690
+ currentFrame = 0
691
+ }
692
+ }
693
+ get {
694
+ if let animation {
695
+ animation.time(forFrame: currentFrame)
696
+ } else {
697
+ 0
698
+ }
699
+ }
700
+ }
701
+
702
+ /// Sets the current animation time with a frame in the animations framerate.
703
+ ///
704
+ /// Note: Setting this will stop the current animation, if any.
705
+ public var currentFrame: AnimationFrameTime {
706
+ set {
707
+ removeCurrentAnimationIfNecessary()
708
+ updateAnimationFrame(newValue)
709
+ currentPlaybackMode = .paused(at: .frame(currentFrame))
710
+ }
711
+ get {
712
+ rootAnimationLayer?.currentFrame ?? 0
713
+ }
714
+ }
715
+
716
+ /// Returns the current animation frame while an animation is playing.
717
+ public var realtimeAnimationFrame: AnimationFrameTime {
718
+ isAnimationPlaying ? rootAnimationLayer?.presentation()?.currentFrame ?? currentFrame : currentFrame
719
+ }
720
+
721
+ /// Returns the current animation frame while an animation is playing.
722
+ public var realtimeAnimationProgress: AnimationProgressTime {
723
+ if let animation {
724
+ return animation.progressTime(forFrame: realtimeAnimationFrame)
725
+ }
726
+ return 0
727
+ }
728
+
729
+ /// Sets the speed of the animation playback. Defaults to 1
730
+ public var animationSpeed: CGFloat = 1 {
731
+ didSet {
732
+ updateInFlightAnimation()
733
+ }
734
+ }
735
+
736
+ /// When `true` the animation will play back at the framerate encoded in the
737
+ /// `LottieAnimation` model. When `false` the animation will play at the framerate
738
+ /// of the device.
739
+ ///
740
+ /// Defaults to false
741
+ public var respectAnimationFrameRate = false {
742
+ didSet {
743
+ rootAnimationLayer?.respectAnimationFrameRate = respectAnimationFrameRate
744
+ }
745
+ }
746
+
747
+ /// The rendering engine currently being used by this layer.
748
+ /// - This will only be `nil` in cases where the configuration is `automatic`
749
+ /// but a `RootAnimationLayer` hasn't been constructed yet
750
+ public var currentRenderingEngine: RenderingEngine? {
751
+ switch configuration.renderingEngine {
752
+ case .specific(let engine):
753
+ return engine
754
+
755
+ case .automatic:
756
+ guard let animationLayer else {
757
+ return nil
758
+ }
759
+
760
+ if animationLayer is CoreAnimationLayer {
761
+ return .coreAnimation
762
+ } else {
763
+ return .mainThread
764
+ }
765
+ }
766
+ }
767
+
768
+ /// Whether or not the Main Thread rendering engine should use `forceDisplayUpdate()`
769
+ /// when rendering each individual frame.
770
+ /// - The main thread rendering engine implements optimizations to decrease the amount
771
+ /// of properties that have to be re-rendered on each frame. There are some cases
772
+ /// where this can result in bugs / incorrect behavior, so we allow it to be disabled.
773
+ /// - Forcing a full render on every frame will decrease performance, and is not recommended
774
+ /// except as a workaround to a bug in the main thread rendering engine.
775
+ /// - Has no effect when using the Core Animation rendering engine.
776
+ public var mainThreadRenderingEngineShouldForceDisplayUpdateOnEachFrame = false {
777
+ didSet {
778
+ (rootAnimationLayer as? MainThreadAnimationLayer)?.forceDisplayUpdateOnEachFrame
779
+ = mainThreadRenderingEngineShouldForceDisplayUpdateOnEachFrame
780
+ }
781
+ }
782
+
783
+ /// Sets the lottie file backing the animation layer. Setting this will clear the
784
+ /// layer's contents, completion blocks and current state. The new animation will
785
+ /// be loaded up and set to the beginning of its timeline.
786
+ /// The loopMode, animationSpeed and imageProvider will be set according
787
+ /// to lottie file settings
788
+ /// - Parameters:
789
+ /// - animationId: Internal animation id to play. Optional
790
+ /// Defaults to play first animation in file.
791
+ /// - dotLottieFile: Lottie file to play
792
+ public func loadAnimation(
793
+ _ animationId: String? = nil,
794
+ from dotLottieFile: DotLottieFile
795
+ ) {
796
+ guard let dotLottieAnimation = dotLottieFile.animation(for: animationId) else { return }
797
+ loadAnimation(dotLottieAnimation)
798
+ }
799
+
800
+ /// Sets the lottie file backing the animation layer. Setting this will clear the
801
+ /// layer's contents, completion blocks and current state. The new animation will
802
+ /// be loaded up and set to the beginning of its timeline.
803
+ /// The loopMode, animationSpeed and imageProvider will be set according
804
+ /// to lottie file settings
805
+ /// - Parameters:
806
+ /// - atIndex: Internal animation index to play.
807
+ /// Defaults to play first animation in file.
808
+ /// - dotLottieFile: Lottie file to play
809
+ public func loadAnimation(
810
+ atIndex index: Int,
811
+ from dotLottieFile: DotLottieFile
812
+ ) {
813
+ guard let dotLottieAnimation = dotLottieFile.animation(at: index) else { return }
814
+ loadAnimation(dotLottieAnimation)
815
+ }
816
+
817
+ /// Reloads the images supplied to the animation from the `imageProvider`
818
+ public func reloadImages() {
819
+ rootAnimationLayer?.reloadImages()
820
+ }
821
+
822
+ /// Forces the LottieAnimationView to redraw its contents.
823
+ public func forceDisplayUpdate() {
824
+ rootAnimationLayer?.forceDisplayUpdate()
825
+ }
826
+
827
+ /// Sets a ValueProvider for the specified keypath. The value provider will be set
828
+ /// on all properties that match the keypath.
829
+ ///
830
+ /// Nearly all properties of a Lottie animation can be changed at runtime using a
831
+ /// combination of `Animation Keypaths` and `Value Providers`.
832
+ /// Setting a ValueProvider on a keypath will cause the animation to update its
833
+ /// contents and read the new Value Provider.
834
+ ///
835
+ /// A value provider provides a typed value on a frame by frame basis.
836
+ ///
837
+ /// - Parameter valueProvider: The new value provider for the properties.
838
+ /// - Parameter keypath: The keypath used to search for properties.
839
+ ///
840
+ /// Example:
841
+ /// ```
842
+ /// /// A keypath that finds the color value for all `Fill 1` nodes.
843
+ /// let fillKeypath = AnimationKeypath(keypath: "**.Fill 1.Color")
844
+ /// /// A Color Value provider that returns a reddish color.
845
+ /// let redValueProvider = ColorValueProvider(Color(r: 1, g: 0.2, b: 0.3, a: 1))
846
+ /// /// Set the provider on the animationView.
847
+ /// animationView.setValueProvider(redValueProvider, keypath: fillKeypath)
848
+ /// ```
849
+ public func setValueProvider(_ valueProvider: AnyValueProvider, keypath: AnimationKeypath) {
850
+ guard let animationLayer = rootAnimationLayer else { return }
851
+
852
+ valueProviders[keypath] = valueProvider
853
+ animationLayer.setValueProvider(valueProvider, keypath: keypath)
854
+ }
855
+
856
+ public func removeValueProvider(for keypath: AnimationKeypath) {
857
+ guard let animationLayer = rootAnimationLayer else { return }
858
+
859
+ for valueProvider in valueProviders {
860
+ guard valueProvider.key.matches(keypath) else { continue }
861
+ valueProviders[valueProvider.key] = nil
862
+ }
863
+ animationLayer.removeValueProvider(for: keypath)
864
+ }
865
+
866
+ /// Reads the value of a property specified by the Keypath.
867
+ /// Returns nil if no property is found.
868
+ ///
869
+ /// - Parameter for: The keypath used to search for the property.
870
+ /// - Parameter atFrame: The Frame Time of the value to query. If nil then the current frame is used.
871
+ public func getValue(for keypath: AnimationKeypath, atFrame: AnimationFrameTime?) -> Any? {
872
+ rootAnimationLayer?.getValue(for: keypath, atFrame: atFrame)
873
+ }
874
+
875
+ /// Reads the original value of a property specified by the Keypath.
876
+ /// This will ignore any value providers and can be useful when implementing a value providers that makes change to the original value from the animation.
877
+ /// Returns nil if no property is found.
878
+ ///
879
+ /// - Parameter for: The keypath used to search for the property.
880
+ /// - Parameter atFrame: The Frame Time of the value to query. If nil then the current frame is used.
881
+ public func getOriginalValue(for keypath: AnimationKeypath, atFrame: AnimationFrameTime?) -> Any? {
882
+ rootAnimationLayer?.getOriginalValue(for: keypath, atFrame: atFrame)
883
+ }
884
+
885
+ /// Logs all child keypaths.
886
+ public func logHierarchyKeypaths() {
887
+ rootAnimationLayer?.logHierarchyKeypaths()
888
+ }
889
+
890
+ /// Computes and returns a list of all child keypaths in the current animation.
891
+ /// The returned list is the same as the log output of `logHierarchyKeypaths()`
892
+ public func allHierarchyKeypaths() -> [String] {
893
+ rootAnimationLayer?.allHierarchyKeypaths() ?? []
894
+ }
895
+
896
+ /// Converts a CGRect from the LottieAnimationView's coordinate space into the
897
+ /// coordinate space of the layer found at Keypath.
898
+ ///
899
+ /// If no layer is found, nil is returned
900
+ ///
901
+ /// - Parameter rect: The CGRect to convert.
902
+ /// - Parameter toLayerAt: The keypath used to find the layer.
903
+ public func convert(_ rect: CGRect, toLayerAt keypath: AnimationKeypath?) -> CGRect? {
904
+ guard let animationLayer = rootAnimationLayer else { return nil }
905
+ guard let keypath else {
906
+ return convert(rect, to: animationLayer)
907
+ }
908
+ guard let sublayer = animationLayer.layer(for: keypath) else {
909
+ return nil
910
+ }
911
+ setNeedsLayout()
912
+ layoutIfNeeded()
913
+ forceDisplayUpdate()
914
+ return animationLayer.convert(rect, to: sublayer)
915
+ }
916
+
917
+ /// Converts a CGPoint from the LottieAnimationView's coordinate space into the
918
+ /// coordinate space of the layer found at Keypath.
919
+ ///
920
+ /// If no layer is found, nil is returned
921
+ ///
922
+ /// - Parameter point: The CGPoint to convert.
923
+ /// - Parameter toLayerAt: The keypath used to find the layer.
924
+ public func convert(_ point: CGPoint, toLayerAt keypath: AnimationKeypath?) -> CGPoint? {
925
+ guard let animationLayer = rootAnimationLayer else { return nil }
926
+ guard let keypath else {
927
+ return convert(point, to: animationLayer)
928
+ }
929
+ guard let sublayer = animationLayer.layer(for: keypath) else {
930
+ return nil
931
+ }
932
+ setNeedsLayout()
933
+ layoutIfNeeded()
934
+ forceDisplayUpdate()
935
+ return animationLayer.convert(point, to: sublayer)
936
+ }
937
+
938
+ /// Sets the enabled state of all animator nodes found with the keypath search.
939
+ /// This can be used to interactively enable / disable parts of the animation.
940
+ ///
941
+ /// - Parameter isEnabled: When true the animator nodes affect the rendering tree. When false the node is removed from the tree.
942
+ /// - Parameter keypath: The keypath used to find the node(s).
943
+ public func setNodeIsEnabled(isEnabled: Bool, keypath: AnimationKeypath) {
944
+ guard let animationLayer = rootAnimationLayer else { return }
945
+ let nodes = animationLayer.animatorNodes(for: keypath)
946
+ if let nodes {
947
+ for node in nodes {
948
+ node.isEnabled = isEnabled
949
+ }
950
+ forceDisplayUpdate()
951
+ }
952
+ }
953
+
954
+ /// Markers are a way to describe a point in time by a key name.
955
+ ///
956
+ /// Markers are encoded into animation JSON. By using markers a designer can mark
957
+ /// playback points for a developer to use without having to worry about keeping
958
+ /// track of animation frames. If the animation file is updated, the developer
959
+ /// does not need to update playback code.
960
+ ///
961
+ /// Returns the Progress Time for the marker named. Returns nil if no marker found.
962
+ public func progressTime(forMarker named: String) -> AnimationProgressTime? {
963
+ guard let animation else {
964
+ return nil
965
+ }
966
+ return animation.progressTime(forMarker: named)
967
+ }
968
+
969
+ /// Markers are a way to describe a point in time by a key name.
970
+ ///
971
+ /// Markers are encoded into animation JSON. By using markers a designer can mark
972
+ /// playback points for a developer to use without having to worry about keeping
973
+ /// track of animation frames. If the animation file is updated, the developer
974
+ /// does not need to update playback code.
975
+ ///
976
+ /// Returns the Frame Time for the marker named. Returns nil if no marker found.
977
+ public func frameTime(forMarker named: String) -> AnimationFrameTime? {
978
+ guard let animation else {
979
+ return nil
980
+ }
981
+ return animation.frameTime(forMarker: named)
982
+ }
983
+
984
+ /// Markers are a way to describe a point in time and a duration by a key name.
985
+ ///
986
+ /// Markers are encoded into animation JSON. By using markers a designer can mark
987
+ /// playback points for a developer to use without having to worry about keeping
988
+ /// track of animation frames. If the animation file is updated, the developer
989
+ /// does not need to update playback code.
990
+ ///
991
+ /// - Returns: The duration frame time for the marker, or `nil` if no marker found.
992
+ public func durationFrameTime(forMarker named: String) -> AnimationFrameTime? {
993
+ guard let animation else {
994
+ return nil
995
+ }
996
+ return animation.durationFrameTime(forMarker: named)
997
+ }
998
+
999
+ public func updateAnimationForBackgroundState(backgroundBehavior: LottieBackgroundBehavior? = nil) {
1000
+ if let currentContext = animationContext {
1001
+ let effectiveBehavior = backgroundBehavior ?? self.backgroundBehavior
1002
+ switch effectiveBehavior {
1003
+ case .stop:
1004
+ removeCurrentAnimation()
1005
+ updateAnimationFrame(currentContext.playFrom)
1006
+
1007
+ case .pause:
1008
+ removeCurrentAnimation()
1009
+
1010
+ case .pauseAndRestore:
1011
+ currentContext.closure.ignoreDelegate = true
1012
+ removeCurrentAnimation()
1013
+ /// Keep the stale context around for when the app enters the foreground.
1014
+ animationContext = currentContext
1015
+
1016
+ case .forceFinish:
1017
+ removeCurrentAnimation()
1018
+ updateAnimationFrame(currentContext.playTo)
1019
+
1020
+ case .continuePlaying:
1021
+ break
1022
+ }
1023
+ }
1024
+ }
1025
+
1026
+ public func updateAnimationForForegroundState(
1027
+ wasWaitingToPlayAnimation: Bool,
1028
+ backgroundBehavior: LottieBackgroundBehavior? = nil
1029
+ ) {
1030
+ if let currentContext = animationContext {
1031
+ let effectiveBehavior = backgroundBehavior ?? self.backgroundBehavior
1032
+ if wasWaitingToPlayAnimation {
1033
+ addNewAnimationForContext(currentContext)
1034
+ } else if effectiveBehavior == .pauseAndRestore {
1035
+ /// Restore animation from saved state
1036
+ updateInFlightAnimation()
1037
+ }
1038
+ }
1039
+ }
1040
+
1041
+ // MARK: Internal
1042
+
1043
+ var rootAnimationLayer: RootAnimationLayer? = nil
1044
+
1045
+ /// Context describing the animation that is currently playing in this `LottieAnimationView`
1046
+ /// - When non-nil, an animation is currently playing in this layer. Otherwise,
1047
+ /// the layer is paused on a specific frame.
1048
+ fileprivate(set) var animationContext: AnimationContext?
1049
+
1050
+ var hasAnimationContext: Bool {
1051
+ animationContext != nil
1052
+ }
1053
+
1054
+ /// Set animation name from Interface Builder
1055
+ var animationName: String? {
1056
+ didSet {
1057
+ animation = animationName.flatMap {
1058
+ LottieAnimation.named($0, animationCache: nil)
1059
+ }
1060
+ }
1061
+ }
1062
+
1063
+ /// Updates the animation frame. Does not affect any current animations
1064
+ func updateAnimationFrame(_ newFrame: CGFloat) {
1065
+ // In performance tests, we have to wrap the animation layer setup
1066
+ // in a `CATransaction` in order for the layers to be deallocated at
1067
+ // the correct time. The `CATransaction`s in this method interfere
1068
+ // with the ones managed by the performance test, and aren't actually
1069
+ // necessary in a headless environment, so we disable them.
1070
+ if TestHelpers.performanceTestsAreRunning {
1071
+ rootAnimationLayer?.currentFrame = newFrame
1072
+ rootAnimationLayer?.forceDisplayUpdate()
1073
+ return
1074
+ }
1075
+
1076
+ CATransaction.begin()
1077
+ CATransaction.setCompletionBlock {
1078
+ self.rootAnimationLayer?.forceDisplayUpdate()
1079
+ }
1080
+ CATransaction.setDisableActions(true)
1081
+ rootAnimationLayer?.currentFrame = newFrame
1082
+ CATransaction.commit()
1083
+ }
1084
+
1085
+ /// Updates an in flight animation.
1086
+ func updateInFlightAnimation() {
1087
+ guard let animationContext else { return }
1088
+
1089
+ guard animationContext.closure.animationState != .complete else {
1090
+ // Tried to re-add an already completed animation. Cancel.
1091
+ self.animationContext = nil
1092
+ return
1093
+ }
1094
+
1095
+ /// Tell existing context to ignore its closure
1096
+ animationContext.closure.ignoreDelegate = true
1097
+
1098
+ /// Make a new context, stealing the completion block from the previous.
1099
+ let newContext = AnimationContext(
1100
+ playFrom: animationContext.playFrom,
1101
+ playTo: animationContext.playTo,
1102
+ framerate: animationContext.framerate,
1103
+ closure: animationContext.closure.completionBlock
1104
+ )
1105
+
1106
+ /// Remove current animation, and freeze the current frame.
1107
+ let pauseFrame = realtimeAnimationFrame
1108
+ rootAnimationLayer?.removeAnimation(forKey: activeAnimationName)
1109
+ rootAnimationLayer?.currentFrame = pauseFrame
1110
+
1111
+ addNewAnimationForContext(newContext)
1112
+ }
1113
+
1114
+ func updateRasterizationState() {
1115
+ if isAnimationPlaying {
1116
+ animationLayer?.shouldRasterize = false
1117
+ } else {
1118
+ animationLayer?.shouldRasterize = shouldRasterizeWhenIdle
1119
+ }
1120
+ }
1121
+
1122
+ func loadAnimation(_ animationSource: LottieAnimationSource?) {
1123
+ switch animationSource {
1124
+ case .lottieAnimation(let animation):
1125
+ self.animation = animation
1126
+ case .dotLottieFile(let dotLottieFile):
1127
+ loadAnimation(from: dotLottieFile)
1128
+ case nil:
1129
+ animation = nil
1130
+ }
1131
+ }
1132
+
1133
+ // MARK: Fileprivate
1134
+
1135
+ fileprivate var _activeAnimationName: String = LottieAnimationLayer.animationName
1136
+ fileprivate var animationID = 0
1137
+
1138
+ fileprivate var activeAnimationName: String {
1139
+ switch rootAnimationLayer?.primaryAnimationKey {
1140
+ case .specific(let animationKey):
1141
+ animationKey
1142
+ case .managed, nil:
1143
+ _activeAnimationName
1144
+ }
1145
+ }
1146
+
1147
+ /// Stops the current in flight animation and freezes the animation in its current state.
1148
+ fileprivate func removeCurrentAnimation() {
1149
+ guard animationContext != nil else { return }
1150
+ let pauseFrame = realtimeAnimationFrame
1151
+ animationLayer?.removeAnimation(forKey: activeAnimationName)
1152
+ updateAnimationFrame(pauseFrame)
1153
+ animationContext = nil
1154
+ }
1155
+
1156
+ fileprivate func makeAnimationLayer(usingEngine renderingEngine: RenderingEngineOption) {
1157
+ /// Disable the default implicit crossfade animation Core Animation creates
1158
+ /// when adding or removing sublayers.
1159
+ actions = ["sublayers": NSNull()]
1160
+
1161
+ /// Remove current animation if any
1162
+ removeCurrentAnimation()
1163
+
1164
+ if let oldAnimation = animationLayer {
1165
+ oldAnimation.removeFromSuperlayer()
1166
+ rootAnimationLayer = nil
1167
+ }
1168
+
1169
+ guard let animation else {
1170
+ return
1171
+ }
1172
+ let rootAnimationLayer: RootAnimationLayer? =
1173
+ switch renderingEngine {
1174
+ case .automatic:
1175
+ makeAutomaticEngineLayer(for: animation)
1176
+ case .specific(.coreAnimation):
1177
+ makeCoreAnimationLayer(for: animation)
1178
+ case .specific(.mainThread):
1179
+ makeMainThreadAnimationLayer(for: animation)
1180
+ }
1181
+
1182
+ guard let animationLayer = rootAnimationLayer else {
1183
+ return
1184
+ }
1185
+
1186
+ animationLayer.lottieAnimationLayer = self
1187
+
1188
+ for (keypath, valueProvider) in valueProviders {
1189
+ animationLayer.setValueProvider(valueProvider, keypath: keypath)
1190
+ }
1191
+
1192
+ animationLayerDidLoad?(self, renderingEngine)
1193
+
1194
+ animationLayer.renderScale = screenScale
1195
+
1196
+ addSublayer(animationLayer)
1197
+ self.rootAnimationLayer = animationLayer
1198
+ reloadImages()
1199
+ animationLayer.setNeedsDisplay()
1200
+ setNeedsLayout()
1201
+ currentFrame = CGFloat(animation.startFrame)
1202
+ }
1203
+
1204
+ fileprivate func makeMainThreadAnimationLayer(for animation: LottieAnimation) -> MainThreadAnimationLayer {
1205
+ let mainThreadAnimationLayer = MainThreadAnimationLayer(
1206
+ animation: animation,
1207
+ imageProvider: imageProvider.cachedImageProvider,
1208
+ textProvider: textProvider,
1209
+ fontProvider: fontProvider,
1210
+ maskAnimationToBounds: maskAnimationToBounds,
1211
+ logger: logger
1212
+ )
1213
+
1214
+ mainThreadAnimationLayer.forceDisplayUpdateOnEachFrame = mainThreadRenderingEngineShouldForceDisplayUpdateOnEachFrame
1215
+ return mainThreadAnimationLayer
1216
+ }
1217
+
1218
+ fileprivate func makeCoreAnimationLayer(for animation: LottieAnimation) -> CoreAnimationLayer? {
1219
+ do {
1220
+ let coreAnimationLayer = try CoreAnimationLayer(
1221
+ animation: animation,
1222
+ imageProvider: imageProvider.cachedImageProvider,
1223
+ textProvider: textProvider,
1224
+ fontProvider: fontProvider,
1225
+ maskAnimationToBounds: maskAnimationToBounds,
1226
+ compatibilityTrackerMode: .track,
1227
+ logger: logger
1228
+ )
1229
+
1230
+ coreAnimationLayer.didSetUpAnimation = { [logger] compatibilityIssues in
1231
+ logger.assert(
1232
+ compatibilityIssues.isEmpty,
1233
+ "Encountered Core Animation compatibility issues while setting up animation:\n"
1234
+ + compatibilityIssues.map { $0.description }.joined(separator: "\n") + "\n\n"
1235
+ + """
1236
+ This animation cannot be rendered correctly by the Core Animation engine.
1237
+ To resolve this issue, you can use `RenderingEngineOption.automatic`, which automatically falls back
1238
+ to the Main Thread rendering engine when necessary, or just use `RenderingEngineOption.mainThread`.
1239
+
1240
+ """
1241
+ )
1242
+ }
1243
+
1244
+ return coreAnimationLayer
1245
+ } catch {
1246
+ // This should never happen, because we initialize the `CoreAnimationLayer` with
1247
+ // `CompatibilityTracker.Mode.track` (which reports errors in `didSetUpAnimation`,
1248
+ // not by throwing).
1249
+ logger.assertionFailure("Encountered unexpected error \(error)")
1250
+ return nil
1251
+ }
1252
+ }
1253
+
1254
+ fileprivate func makeAutomaticEngineLayer(for animation: LottieAnimation) -> CoreAnimationLayer? {
1255
+ do {
1256
+ // Attempt to set up the Core Animation layer. This can either throw immediately in `init`,
1257
+ // or throw an error later in `CALayer.display()` that will be reported in `didSetUpAnimation`.
1258
+ let coreAnimationLayer = try CoreAnimationLayer(
1259
+ animation: animation,
1260
+ imageProvider: imageProvider.cachedImageProvider,
1261
+ textProvider: textProvider,
1262
+ fontProvider: fontProvider,
1263
+ maskAnimationToBounds: maskAnimationToBounds,
1264
+ compatibilityTrackerMode: .abort,
1265
+ logger: logger
1266
+ )
1267
+
1268
+ coreAnimationLayer.didSetUpAnimation = { [weak self] issues in
1269
+ self?.automaticEngineLayerDidSetUpAnimation(issues)
1270
+ }
1271
+
1272
+ return coreAnimationLayer
1273
+ } catch {
1274
+ if case CompatibilityTracker.Error.encounteredCompatibilityIssue(let compatibilityIssue) = error {
1275
+ automaticEngineLayerDidSetUpAnimation([compatibilityIssue])
1276
+ } else {
1277
+ // This should never happen, because we expect `CoreAnimationLayer` to only throw
1278
+ // `CompatibilityTracker.Error.encounteredCompatibilityIssue` errors.
1279
+ logger.assertionFailure("Encountered unexpected error \(error)")
1280
+ automaticEngineLayerDidSetUpAnimation([])
1281
+ }
1282
+
1283
+ return nil
1284
+ }
1285
+ }
1286
+
1287
+ /// Handles any compatibility issues with the Core Animation engine
1288
+ /// by falling back to the Main Thread engine
1289
+ fileprivate func automaticEngineLayerDidSetUpAnimation(_ compatibilityIssues: [CompatibilityIssue]) {
1290
+ // If there weren't any compatibility issues, then there's nothing else to do
1291
+ if compatibilityIssues.isEmpty {
1292
+ return
1293
+ }
1294
+
1295
+ logger.warn(
1296
+ "Encountered Core Animation compatibility issue while setting up animation:\n"
1297
+ + compatibilityIssues.map { $0.description }.joined(separator: "\n") + "\n"
1298
+ + """
1299
+ This animation may have additional compatibility issues, but animation setup was cancelled early to avoid wasted work.
1300
+
1301
+ Automatically falling back to Main Thread rendering engine. This fallback comes with some additional performance
1302
+ overhead, which can be reduced by manually specifying that this animation should always use the Main Thread engine.
1303
+
1304
+ """
1305
+ )
1306
+
1307
+ let animationContext = animationContext
1308
+ let currentFrame = currentFrame
1309
+
1310
+ // Disable the completion handler delegate before tearing down the `CoreAnimationLayer`
1311
+ // and building the `MainThreadAnimationLayer`. Otherwise deinitializing the
1312
+ // `CoreAnimationLayer` would trigger the animation completion handler even though
1313
+ // the animation hasn't even started playing yet.
1314
+ animationContext?.closure.ignoreDelegate = true
1315
+
1316
+ makeAnimationLayer(usingEngine: .mainThread)
1317
+
1318
+ // Set up the Main Thread animation layer using the same configuration that
1319
+ // was being used by the previous Core Animation layer
1320
+ self.currentFrame = currentFrame
1321
+
1322
+ if let animationContext {
1323
+ // `AnimationContext.closure` (`AnimationCompletionDelegate`) is a reference type
1324
+ // that is the animation layer's `CAAnimationDelegate`, and holds a reference to
1325
+ // the animation layer. Reusing a single instance across different animation layers
1326
+ // can cause the animation setup to fail, so we create a copy of the `animationContext`:
1327
+ addNewAnimationForContext(AnimationContext(
1328
+ playFrom: animationContext.playFrom,
1329
+ playTo: animationContext.playTo,
1330
+ framerate: animationContext.framerate,
1331
+ closure: animationContext.closure.completionBlock
1332
+ ))
1333
+ }
1334
+ }
1335
+
1336
+ /// Removes the current animation and pauses the animation at the current frame
1337
+ /// if necessary before setting up a new animation.
1338
+ /// - This is not necessary with the Core Animation engine, and skipping
1339
+ /// this step lets us avoid building the animations twice (once paused
1340
+ /// and once again playing)
1341
+ /// - This method should only be called immediately before setting up another
1342
+ /// animation -- otherwise this LottieAnimationView could be put in an inconsistent state.
1343
+ fileprivate func removeCurrentAnimationIfNecessary() {
1344
+ switch currentRenderingEngine {
1345
+ case .mainThread:
1346
+ removeCurrentAnimation()
1347
+ case .coreAnimation, nil:
1348
+ // We still need to remove the `animationContext`, since it should only be present
1349
+ // when an animation is actually playing. Without this calling `removeCurrentAnimationIfNecessary()`
1350
+ // and then setting the animation to a specific paused frame would put this
1351
+ // `LottieAnimationView` in an inconsistent state.
1352
+ animationContext = nil
1353
+ }
1354
+ }
1355
+
1356
+ /// Adds animation to animation layer and sets the delegate. If animation layer or animation are nil, exits.
1357
+ fileprivate func addNewAnimationForContext(_ animationContext: AnimationContext) {
1358
+ guard let animationlayer = rootAnimationLayer, let animation else {
1359
+ return
1360
+ }
1361
+
1362
+ self.animationContext = animationContext
1363
+
1364
+ animationID = animationID + 1
1365
+ _activeAnimationName = LottieAnimationLayer.animationName + String(animationID)
1366
+
1367
+ if let coreAnimationLayer = animationlayer as? CoreAnimationLayer {
1368
+ var animationContext = animationContext
1369
+
1370
+ // Core Animation doesn't natively support negative speed values,
1371
+ // so instead we can swap `playFrom` / `playTo`
1372
+ if animationSpeed < 0 {
1373
+ let temp = animationContext.playFrom
1374
+ animationContext.playFrom = animationContext.playTo
1375
+ animationContext.playTo = temp
1376
+ }
1377
+
1378
+ var timingConfiguration = CoreAnimationLayer.CAMediaTimingConfiguration(
1379
+ autoreverses: loopMode.caAnimationConfiguration.autoreverses,
1380
+ repeatCount: loopMode.caAnimationConfiguration.repeatCount,
1381
+ speed: abs(Float(animationSpeed))
1382
+ )
1383
+
1384
+ // The animation should start playing from the `currentFrame`,
1385
+ // if `currentFrame` is included in the time range being played.
1386
+ let lowerBoundTime = min(animationContext.playFrom, animationContext.playTo)
1387
+ let upperBoundTime = max(animationContext.playFrom, animationContext.playTo)
1388
+ if (lowerBoundTime ..< upperBoundTime).contains(trunc(currentFrame)) {
1389
+ // We have to configure this differently depending on the loop mode:
1390
+ switch loopMode {
1391
+ // When playing exactly once (and not looping), we can just set the
1392
+ // `playFrom` time to be the `currentFrame`. Since the animation duration
1393
+ // is based on `playFrom` and `playTo`, this automatically truncates the
1394
+ // duration (so the animation stops playing at `playFrom`).
1395
+ // - Don't do this if the animation is already at that frame
1396
+ // (e.g. playing from 100% to 0% when the animation is already at 0%)
1397
+ // since that would cause the animation to not play at all.
1398
+ case .playOnce:
1399
+ if animationContext.playTo != currentFrame {
1400
+ animationContext.playFrom = currentFrame
1401
+ }
1402
+
1403
+ // When looping, we specifically _don't_ want to affect the duration of the animation,
1404
+ // since that would affect the duration of all subsequent loops. We just want to adjust
1405
+ // the duration of the _first_ loop. Instead of setting `playFrom`, we just add a `timeOffset`
1406
+ // so the first loop begins at `currentTime` but all subsequent loops are the standard duration.
1407
+ default:
1408
+ if animationSpeed < 0 {
1409
+ timingConfiguration.timeOffset = animation.time(forFrame: animationContext.playFrom) - currentTime
1410
+ } else {
1411
+ timingConfiguration.timeOffset = currentTime - animation.time(forFrame: animationContext.playFrom)
1412
+ }
1413
+ }
1414
+ }
1415
+
1416
+ // If attempting to play a zero-duration animation, just pause on that single frame instead
1417
+ if animationContext.playFrom == animationContext.playTo {
1418
+ currentFrame = animationContext.playTo
1419
+ animationContext.closure.completionBlock?(true)
1420
+ return
1421
+ }
1422
+
1423
+ coreAnimationLayer.playAnimation(configuration: .init(
1424
+ animationContext: animationContext,
1425
+ timingConfiguration: timingConfiguration
1426
+ ))
1427
+
1428
+ return
1429
+ }
1430
+
1431
+ /// At this point there is no animation on animationLayer and its state is set.
1432
+
1433
+ let framerate = animation.framerate
1434
+
1435
+ let playFrom = animationContext.playFrom.clamp(animation.startFrame, animation.endFrame)
1436
+ let playTo = animationContext.playTo.clamp(animation.startFrame, animation.endFrame)
1437
+
1438
+ let duration = ((max(playFrom, playTo) - min(playFrom, playTo)) / CGFloat(framerate))
1439
+
1440
+ let playingForward: Bool =
1441
+ (
1442
+ (animationSpeed > 0 && playFrom < playTo) ||
1443
+ (animationSpeed < 0 && playTo < playFrom)
1444
+ )
1445
+
1446
+ var startFrame = currentFrame.clamp(min(playFrom, playTo), max(playFrom, playTo))
1447
+ if startFrame == playTo {
1448
+ startFrame = playFrom
1449
+ }
1450
+
1451
+ let timeOffset: TimeInterval = playingForward
1452
+ ? Double(startFrame - min(playFrom, playTo)) / framerate
1453
+ : Double(max(playFrom, playTo) - startFrame) / framerate
1454
+
1455
+ let layerAnimation = CABasicAnimation(keyPath: "currentFrame")
1456
+ layerAnimation.fromValue = playFrom
1457
+ layerAnimation.toValue = playTo
1458
+ layerAnimation.speed = Float(animationSpeed)
1459
+ layerAnimation.duration = TimeInterval(duration)
1460
+ layerAnimation.fillMode = CAMediaTimingFillMode.both
1461
+ layerAnimation.repeatCount = loopMode.caAnimationConfiguration.repeatCount
1462
+ layerAnimation.autoreverses = loopMode.caAnimationConfiguration.autoreverses
1463
+
1464
+ layerAnimation.isRemovedOnCompletion = false
1465
+ if timeOffset != 0 {
1466
+ let currentLayerTime = convertTime(CACurrentMediaTime(), from: nil)
1467
+ layerAnimation.beginTime = currentLayerTime - (timeOffset * 1 / Double(abs(animationSpeed)))
1468
+ }
1469
+ layerAnimation.delegate = animationContext.closure
1470
+ animationContext.closure.animationLayer = animationlayer
1471
+ animationContext.closure.animationKey = activeAnimationName
1472
+
1473
+ animationlayer.add(layerAnimation, forKey: activeAnimationName)
1474
+ updateRasterizationState()
1475
+ }
1476
+
1477
+ // MARK: Private
1478
+
1479
+ static private let animationName = "Lottie"
1480
+
1481
+ private let logger: LottieLogger
1482
+
1483
+ /// The `LottieBackgroundBehavior` that was specified manually by setting `self.backgroundBehavior`
1484
+ private var _backgroundBehavior: LottieBackgroundBehavior?
1485
+
1486
+ /// Whether or not the current animation should be overridden with
1487
+ /// the marker matching the current "reduced motion" mode.
1488
+ private var shouldOverrideWithReducedMotionAnimation: Bool {
1489
+ reducedMotionMarker != nil
1490
+ }
1491
+
1492
+ /// The marker that corresponds to the current "reduced motion" mode.
1493
+ private var reducedMotionMarker: Marker? {
1494
+ switch configuration.reducedMotionOption.currentReducedMotionMode {
1495
+ case .standardMotion, .disabledMotion:
1496
+ nil
1497
+ case .reducedMotion:
1498
+ animation?.reducedMotionMarker
1499
+ }
1500
+ }
1501
+
1502
+ /// Checks if static rendering or reduced motion is enabled and renders as appropriate.
1503
+ /// - Returns: `true` if override happened and the caller should return early.
1504
+ private func overrideReducedMotionIfNeeded(completion: LottieCompletionBlock?) -> Bool {
1505
+ switch configuration.reducedMotionOption {
1506
+ case .disabledMotion:
1507
+ renderStaticFrame(completion: completion)
1508
+ return true
1509
+
1510
+ case .reducedMotion:
1511
+ playReducedMotionAnimation(completion: completion)
1512
+ return true
1513
+
1514
+ case .standardMotion, .specific(_), .dynamic(_, dataID: _):
1515
+ return false
1516
+ }
1517
+ }
1518
+
1519
+ private func loadAnimation(_ dotLottieAnimation: DotLottieFile.Animation) {
1520
+ loopMode = dotLottieAnimation.configuration.loopMode
1521
+ animationSpeed = CGFloat(dotLottieAnimation.configuration.speed)
1522
+
1523
+ if let imageProvider = dotLottieAnimation.configuration.imageProvider {
1524
+ self.imageProvider = imageProvider
1525
+ }
1526
+
1527
+ animation = dotLottieAnimation.animation
1528
+ }
1529
+
1530
+ /// Plays the marker that corresponds to the current "reduced motion" mode if present.
1531
+ private func playReducedMotionAnimation(completion: LottieCompletionBlock?) {
1532
+ guard let reducedMotionMarker else { return }
1533
+
1534
+ // `play(marker:)` calls the `play(fromFrame:toFrame:)` method which calls this
1535
+ // `playReducedMotionAnimation` method when `shouldOverrideWithReducedMotionAnimation`
1536
+ // is `true`. To prevent infinite recursion, disable the reduced motion functionality
1537
+ // until the end of this function.
1538
+ let currentConfiguration = configuration
1539
+ configuration.reducedMotionOption = .standardMotion
1540
+ defer { configuration = currentConfiguration }
1541
+
1542
+ play(marker: reducedMotionMarker.name, completion: completion)
1543
+ }
1544
+
1545
+ /// Renders the animation statically at a single frame without playing.
1546
+ /// Used when `disabledMotion` mode is enabled for snapshot testing.
1547
+ /// Prefers the first frame of the reduced motion marker if present,
1548
+ /// otherwise falls back to the animation's start frame.
1549
+ private func renderStaticFrame(completion: LottieCompletionBlock?) {
1550
+ guard let animation else { return }
1551
+ removeCurrentAnimationIfNecessary()
1552
+
1553
+ // Use the first frame of the reduced motion marker if present,
1554
+ // otherwise fall back to the animation's start frame
1555
+ let staticFrame = animation.reducedMotionMarker?.frameTime ?? animation.startFrame
1556
+
1557
+ currentFrame = staticFrame
1558
+ currentPlaybackMode = .paused(at: .frame(staticFrame))
1559
+ completion?(true)
1560
+ }
1561
+
1562
+ }
1563
+
1564
+ // MARK: - LottieLoopMode + caAnimationConfiguration
1565
+
1566
+ extension LottieLoopMode {
1567
+ /// The `CAAnimation` configuration that reflects this mode
1568
+ var caAnimationConfiguration: (repeatCount: Float, autoreverses: Bool) {
1569
+ switch self {
1570
+ case .playOnce:
1571
+ (repeatCount: 1, autoreverses: false)
1572
+ case .loop:
1573
+ (repeatCount: .greatestFiniteMagnitude, autoreverses: false)
1574
+ case .autoReverse:
1575
+ (repeatCount: .greatestFiniteMagnitude, autoreverses: true)
1576
+ case .repeat(let amount):
1577
+ (repeatCount: amount, autoreverses: false)
1578
+ case .repeatBackwards(let amount):
1579
+ (repeatCount: amount, autoreverses: true)
1580
+ }
1581
+ }
1582
+ }