lottie-ios 3.2.3 → 3.4.2
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.
- package/.github/actions/setup/action.yml +32 -0
- package/.github/issue_template.md +6 -23
- package/.github/workflows/main.yml +98 -0
- package/.github/workflows/stale_issues.yml +17 -0
- package/.spi.yml +6 -0
- package/.swiftpm/xcode/package.xcworkspace/contents.xcworkspacedata +7 -0
- package/.swiftpm/xcode/package.xcworkspace/xcuserdata/cal.xcuserdatad/UserInterfaceState.xcuserstate +0 -0
- package/.swiftpm/xcode/package.xcworkspace/xcuserdata/calstephens.xcuserdatad/UserInterfaceState.xcuserstate +0 -0
- package/.swiftpm/xcode/xcuserdata/{brandonwithrow.xcuserdatad → cal.xcuserdatad}/xcschemes/xcschememanagement.plist +25 -15
- package/.swiftpm/xcode/xcuserdata/calstephens.xcuserdatad/xcschemes/xcschememanagement.plist +14 -0
- package/Gemfile +4 -0
- package/Gemfile.lock +102 -0
- package/Lottie.xcodeproj/project.pbxproj +2011 -1972
- package/Lottie.xcodeproj/project.xcworkspace/contents.xcworkspacedata +1 -1
- package/Lottie.xcodeproj/project.xcworkspace/xcuserdata/cal.xcuserdatad/UserInterfaceState.xcuserstate +0 -0
- package/Lottie.xcodeproj/xcshareddata/xcschemes/{Lottie_iOS.xcscheme → Lottie (iOS).xcscheme } +15 -18
- package/Lottie.xcodeproj/xcshareddata/xcschemes/{Lottie_tvOS.xcscheme → Lottie (macOS).xcscheme } +5 -18
- package/Lottie.xcodeproj/xcshareddata/xcschemes/{Lottie_macOS.xcscheme → Lottie (tvOS).xcscheme } +5 -18
- package/Lottie.xcodeproj/xcuserdata/cal.xcuserdatad/xcschemes/xcschememanagement.plist +37 -0
- package/Lottie.xcodeproj/xcuserdata/calstephens.xcuserdatad/xcschemes/xcschememanagement.plist +24 -0
- package/Lottie.xcworkspace/contents.xcworkspacedata +10 -0
- package/Lottie.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist +8 -0
- package/Lottie.xcworkspace/xcshareddata/swiftpm/Package.resolved +34 -0
- package/Lottie.xcworkspace/xcuserdata/cal.xcuserdatad/UserInterfaceState.xcuserstate +0 -0
- package/{Lottie.xcodeproj/xcuserdata/brandonwithrow.xcuserdatad → Lottie.xcworkspace/xcuserdata/cal.xcuserdatad}/xcdebugger/Breakpoints_v2.xcbkptlist +2 -2
- package/Lottie.xcworkspace/xcuserdata/cal.xcuserdatad/xcdebugger/Expressions.xcexplist +153 -0
- package/Lottie.xcworkspace/xcuserdata/calstephens.xcuserdatad/UserInterfaceState.xcuserstate +0 -0
- package/Lottie.xcworkspace/xcuserdata/calstephens.xcuserdatad/xcdebugger/Breakpoints_v2.xcbkptlist +22 -0
- package/Lottie.xcworkspace/xcuserdata/calstephens.xcuserdatad/xcdebugger/Expressions.xcexplist +138 -0
- package/Lottie.xcworkspace/xcuserdata/calstephens.xcuserdatad/xcschemes/xcschememanagement.plist +30 -0
- package/Package.swift +5 -15
- package/README.md +40 -60
- package/Rakefile +160 -0
- package/Sources/Private/CoreAnimation/Animations/CAAnimation+TimingConfiguration.swift +81 -0
- package/Sources/Private/CoreAnimation/Animations/CALayer+addAnimation.swift +447 -0
- package/Sources/Private/CoreAnimation/Animations/CombinedShapeAnimation.swift +81 -0
- package/Sources/Private/CoreAnimation/Animations/CustomPathAnimation.swift +41 -0
- package/Sources/Private/CoreAnimation/Animations/EllipseAnimation.swift +55 -0
- package/Sources/Private/CoreAnimation/Animations/GradientAnimations.swift +210 -0
- package/Sources/Private/CoreAnimation/Animations/LayerProperty.swift +229 -0
- package/Sources/Private/CoreAnimation/Animations/OpacityAnimation.swift +52 -0
- package/Sources/Private/CoreAnimation/Animations/RectangleAnimation.swift +58 -0
- package/Sources/Private/CoreAnimation/Animations/ShapeAnimation.swift +257 -0
- package/Sources/Private/CoreAnimation/Animations/StarAnimation.swift +124 -0
- package/Sources/Private/CoreAnimation/Animations/StrokeAnimation.swift +73 -0
- package/Sources/Private/CoreAnimation/Animations/TransformAnimations.swift +205 -0
- package/Sources/Private/CoreAnimation/Animations/VisibilityAnimation.swift +37 -0
- package/Sources/Private/CoreAnimation/CompatibilityTracker.swift +130 -0
- package/Sources/Private/CoreAnimation/CoreAnimationLayer.swift +497 -0
- package/Sources/Private/CoreAnimation/Extensions/CALayer+fillBounds.swift +35 -0
- package/Sources/Private/CoreAnimation/Extensions/KeyframeGroup+exactlyOneKeyframe.swift +37 -0
- package/Sources/Private/CoreAnimation/Extensions/Keyframes+combinedIfPossible.swift +154 -0
- package/Sources/Private/CoreAnimation/Layers/AnimationLayer.swift +83 -0
- package/Sources/Private/CoreAnimation/Layers/BaseAnimationLayer.swift +33 -0
- package/Sources/Private/CoreAnimation/Layers/BaseCompositionLayer.swift +88 -0
- package/Sources/Private/CoreAnimation/Layers/CALayer+setupLayerHierarchy.swift +167 -0
- package/Sources/Private/CoreAnimation/Layers/GradientRenderLayer.swift +94 -0
- package/Sources/Private/CoreAnimation/Layers/ImageLayer.swift +79 -0
- package/Sources/Private/CoreAnimation/Layers/LayerModel+makeAnimationLayer.swift +64 -0
- package/Sources/Private/CoreAnimation/Layers/MaskCompositionLayer.swift +138 -0
- package/Sources/Private/CoreAnimation/Layers/PreCompLayer.swift +140 -0
- package/Sources/Private/CoreAnimation/Layers/RepeaterLayer.swift +85 -0
- package/Sources/Private/CoreAnimation/Layers/ShapeItemLayer.swift +315 -0
- package/Sources/Private/CoreAnimation/Layers/ShapeLayer.swift +390 -0
- package/Sources/Private/CoreAnimation/Layers/SolidLayer.swift +47 -0
- package/Sources/Private/CoreAnimation/Layers/TextLayer.swift +91 -0
- package/Sources/Private/CoreAnimation/Layers/TransformLayer.swift +11 -0
- package/Sources/Private/CoreAnimation/ValueProviderStore.swift +139 -0
- package/{lottie-swift/src/Private → Sources/Private/MainThread}/LayerContainers/CompLayers/CompositionLayer.swift +94 -86
- package/{lottie-swift/src/Private → Sources/Private/MainThread}/LayerContainers/CompLayers/ImageCompositionLayer.swift +24 -21
- package/{lottie-swift/src/Private → Sources/Private/MainThread}/LayerContainers/CompLayers/MaskContainerLayer.swift +75 -54
- package/{lottie-swift/src/Private → Sources/Private/MainThread}/LayerContainers/CompLayers/NullCompositionLayer.swift +5 -5
- package/{lottie-swift/src/Private → Sources/Private/MainThread}/LayerContainers/CompLayers/PreCompositionLayer.swift +59 -43
- package/{lottie-swift/src/Private → Sources/Private/MainThread}/LayerContainers/CompLayers/ShapeCompositionLayer.swift +22 -20
- package/{lottie-swift/src/Private → Sources/Private/MainThread}/LayerContainers/CompLayers/SolidCompositionLayer.swift +26 -17
- package/{lottie-swift/src/Private → Sources/Private/MainThread}/LayerContainers/CompLayers/TextCompositionLayer.swift +42 -36
- package/{lottie-swift/src/Private/LayerContainers/AnimationContainer.swift → Sources/Private/MainThread/LayerContainers/MainThreadAnimationLayer.swift} +200 -140
- package/Sources/Private/MainThread/LayerContainers/Utility/CachedImageProvider.swift +47 -0
- package/{lottie-swift/src/Private → Sources/Private/MainThread}/LayerContainers/Utility/CompositionLayersInitializer.swift +33 -24
- package/{lottie-swift/src/Private/LayerContainers/Utility/TextLayer.swift → Sources/Private/MainThread/LayerContainers/Utility/CoreTextRenderLayer.swift} +149 -106
- package/{lottie-swift/src/Private → Sources/Private/MainThread}/LayerContainers/Utility/InvertedMatteLayer.swift +26 -24
- package/Sources/Private/MainThread/LayerContainers/Utility/LayerFontProvider.swift +41 -0
- package/{lottie-swift/src/Private → Sources/Private/MainThread}/LayerContainers/Utility/LayerImageProvider.swift +20 -16
- package/Sources/Private/MainThread/LayerContainers/Utility/LayerTextProvider.swift +40 -0
- package/{lottie-swift/src/Private → Sources/Private/MainThread}/LayerContainers/Utility/LayerTransformNode.swift +82 -67
- package/{lottie-swift/src/Private → Sources/Private/MainThread}/NodeRenderSystem/Extensions/ItemsExtension.swift +9 -4
- package/{lottie-swift/src/Private → Sources/Private/MainThread}/NodeRenderSystem/NodeProperties/NodeProperty.swift +29 -21
- package/{lottie-swift/src/Private → Sources/Private/MainThread}/NodeRenderSystem/NodeProperties/Protocols/AnyNodeProperty.swift +16 -10
- package/{lottie-swift/src/Private → Sources/Private/MainThread}/NodeRenderSystem/NodeProperties/Protocols/AnyValueContainer.swift +6 -6
- package/{lottie-swift/src/Private → Sources/Private/MainThread}/NodeRenderSystem/NodeProperties/Protocols/KeypathSearchable.swift +5 -5
- package/{lottie-swift/src/Private → Sources/Private/MainThread}/NodeRenderSystem/NodeProperties/Protocols/NodePropertyMap.swift +10 -8
- package/{lottie-swift/src/Private → Sources/Private/MainThread}/NodeRenderSystem/NodeProperties/ValueContainer.swift +25 -21
- package/{lottie-swift/src/Private → Sources/Private/MainThread}/NodeRenderSystem/NodeProperties/ValueProviders/GroupInterpolator.swift +23 -17
- package/{lottie-swift/src/Private → Sources/Private/MainThread}/NodeRenderSystem/NodeProperties/ValueProviders/SingleValueProvider.swift +22 -17
- package/{lottie-swift/src/Private → Sources/Private/MainThread}/NodeRenderSystem/Nodes/ModifierNodes/TrimPathNode.swift +110 -79
- package/{lottie-swift/src/Private → Sources/Private/MainThread}/NodeRenderSystem/Nodes/OutputNodes/GroupOutputNode.swift +22 -16
- package/{lottie-swift/src/Private → Sources/Private/MainThread}/NodeRenderSystem/Nodes/OutputNodes/PassThroughOutputNode.swift +19 -15
- package/{lottie-swift/src/Private → Sources/Private/MainThread}/NodeRenderSystem/Nodes/OutputNodes/PathOutputNode.swift +22 -20
- package/{lottie-swift/src/Private → Sources/Private/MainThread}/NodeRenderSystem/Nodes/OutputNodes/Renderables/FillRenderer.swift +22 -23
- package/Sources/Private/MainThread/NodeRenderSystem/Nodes/OutputNodes/Renderables/GradientFillRenderer.swift +247 -0
- package/{lottie-swift/src/Private → Sources/Private/MainThread}/NodeRenderSystem/Nodes/OutputNodes/Renderables/GradientStrokeRenderer.swift +30 -24
- package/{lottie-swift/src/Private/NodeRenderSystem/Nodes/OutputNodes/Renderables/GradientFillRenderer.swift → Sources/Private/MainThread/NodeRenderSystem/Nodes/OutputNodes/Renderables/LegacyGradientFillRenderer.swift} +93 -66
- package/{lottie-swift/src/Private → Sources/Private/MainThread}/NodeRenderSystem/Nodes/OutputNodes/Renderables/StrokeRenderer.swift +23 -19
- package/Sources/Private/MainThread/NodeRenderSystem/Nodes/PathNodes/EllipseNode.swift +139 -0
- package/Sources/Private/MainThread/NodeRenderSystem/Nodes/PathNodes/PolygonNode.swift +170 -0
- package/{lottie-swift/src/Private → Sources/Private/MainThread}/NodeRenderSystem/Nodes/PathNodes/RectNode.swift +95 -58
- package/{lottie-swift/src/Private → Sources/Private/MainThread}/NodeRenderSystem/Nodes/PathNodes/ShapeNode.swift +42 -29
- package/{lottie-swift/src/Private → Sources/Private/MainThread}/NodeRenderSystem/Nodes/PathNodes/StarNode.swift +108 -69
- package/Sources/Private/MainThread/NodeRenderSystem/Nodes/RenderContainers/GroupNode.swift +155 -0
- package/{lottie-swift/src/Private → Sources/Private/MainThread}/NodeRenderSystem/Nodes/RenderNodes/FillNode.swift +51 -37
- package/{lottie-swift/src/Private → Sources/Private/MainThread}/NodeRenderSystem/Nodes/RenderNodes/GradientFillNode.swift +57 -42
- package/{lottie-swift/src/Private → Sources/Private/MainThread}/NodeRenderSystem/Nodes/RenderNodes/GradientStrokeNode.swift +66 -58
- package/Sources/Private/MainThread/NodeRenderSystem/Nodes/RenderNodes/StrokeNode.swift +169 -0
- package/{lottie-swift/src/Private → Sources/Private/MainThread}/NodeRenderSystem/Nodes/Text/TextAnimatorNode.swift +138 -119
- package/{lottie-swift/src/Private → Sources/Private/MainThread}/NodeRenderSystem/Protocols/AnimatorNode.swift +80 -79
- package/{lottie-swift/src/Private → Sources/Private/MainThread}/NodeRenderSystem/Protocols/PathNode.swift +5 -3
- package/{lottie-swift/src/Private → Sources/Private/MainThread}/NodeRenderSystem/Protocols/RenderNode.swift +22 -17
- package/{lottie-swift/src/Private → Sources/Private/MainThread}/NodeRenderSystem/RenderLayers/ShapeContainerLayer.swift +27 -24
- package/{lottie-swift/src/Private → Sources/Private/MainThread}/NodeRenderSystem/RenderLayers/ShapeRenderLayer.swift +34 -25
- package/Sources/Private/Model/Animation.swift +160 -0
- package/Sources/Private/Model/Assets/Asset.swift +43 -0
- package/{lottie-swift/src → Sources}/Private/Model/Assets/AssetLibrary.swift +40 -13
- package/Sources/Private/Model/Assets/ImageAsset.swift +112 -0
- package/{lottie-swift/src → Sources}/Private/Model/Assets/PrecompAsset.swift +20 -10
- package/Sources/Private/Model/DictionaryInitializable.swift +67 -0
- package/Sources/Private/Model/Extensions/Bundle.swift +34 -0
- package/{lottie-swift/src → Sources}/Private/Model/Extensions/KeyedDecodingContainerExtensions.swift +8 -4
- package/Sources/Private/Model/Keyframes/KeyframeData.swift +113 -0
- package/Sources/Private/Model/Keyframes/KeyframeGroup.swift +222 -0
- package/{lottie-swift/src → Sources}/Private/Model/Layers/ImageLayerModel.swift +21 -11
- package/Sources/Private/Model/Layers/LayerModel.swift +228 -0
- package/{lottie-swift/src → Sources}/Private/Model/Layers/PreCompLayerModel.swift +39 -22
- package/{lottie-swift/src → Sources}/Private/Model/Layers/ShapeLayerModel.swift +23 -12
- package/{lottie-swift/src → Sources}/Private/Model/Layers/SolidLayerModel.swift +31 -19
- package/{lottie-swift/src → Sources}/Private/Model/Layers/TextLayerModel.swift +33 -19
- package/Sources/Private/Model/Objects/DashPattern.swift +44 -0
- package/Sources/Private/Model/Objects/Marker.swift +33 -0
- package/Sources/Private/Model/Objects/Mask.swift +80 -0
- package/Sources/Private/Model/Objects/Transform.swift +179 -0
- package/Sources/Private/Model/ShapeItems/Ellipse.swift +74 -0
- package/Sources/Private/Model/ShapeItems/Fill.swift +74 -0
- package/Sources/Private/Model/ShapeItems/GradientFill.swift +137 -0
- package/Sources/Private/Model/ShapeItems/GradientStroke.swift +185 -0
- package/Sources/Private/Model/ShapeItems/Group.swift +48 -0
- package/{lottie-swift/src → Sources}/Private/Model/ShapeItems/Merge.swift +29 -12
- package/Sources/Private/Model/ShapeItems/Rectangle.swift +72 -0
- package/Sources/Private/Model/ShapeItems/Repeater.swift +135 -0
- package/Sources/Private/Model/ShapeItems/Shape.swift +56 -0
- package/Sources/Private/Model/ShapeItems/ShapeItem.swift +163 -0
- package/Sources/Private/Model/ShapeItems/ShapeTransform.swift +135 -0
- package/Sources/Private/Model/ShapeItems/Star.swift +131 -0
- package/Sources/Private/Model/ShapeItems/Stroke.swift +101 -0
- package/Sources/Private/Model/ShapeItems/Trim.swift +77 -0
- package/Sources/Private/Model/Text/Font.swift +61 -0
- package/{lottie-swift/src → Sources}/Private/Model/Text/Glyph.swift +63 -39
- package/Sources/Private/Model/Text/TextAnimator.swift +164 -0
- package/Sources/Private/Model/Text/TextDocument.swift +123 -0
- package/Sources/Private/RootAnimationLayer.swift +51 -0
- package/{lottie-swift/src → Sources}/Private/Utility/Debugging/AnimatorNodeDebugging.swift +7 -7
- package/{lottie-swift/src → Sources}/Private/Utility/Debugging/LayerDebugging.swift +72 -48
- package/Sources/Private/Utility/Debugging/TestHelpers.swift +10 -0
- package/{lottie-swift/src → Sources}/Private/Utility/Extensions/AnimationKeypathExtension.swift +69 -59
- package/Sources/Private/Utility/Extensions/BlendMode+Filter.swift +31 -0
- package/Sources/Private/Utility/Extensions/CGColor+RGB.swift +22 -0
- package/{lottie-swift/src → Sources}/Private/Utility/Extensions/CGFloatExtensions.swift +70 -67
- package/Sources/Private/Utility/Extensions/DataExtension.swift +27 -0
- package/Sources/Private/Utility/Extensions/MathKit.swift +450 -0
- package/Sources/Private/Utility/Extensions/StringExtensions.swift +38 -0
- package/{lottie-swift/src → Sources}/Private/Utility/Helpers/AnimationContext.swift +42 -16
- package/Sources/Private/Utility/Interpolatable/InterpolatableExtensions.swift +134 -0
- package/{lottie-swift/src → Sources}/Private/Utility/Interpolatable/KeyframeExtensions.swift +13 -10
- package/Sources/Private/Utility/Interpolatable/KeyframeGroup+Extensions.swift +59 -0
- package/{lottie-swift/src/Private/NodeRenderSystem/NodeProperties/ValueProviders → Sources/Private/Utility/Interpolatable}/KeyframeInterpolator.swift +125 -108
- package/{lottie-swift/src → Sources}/Private/Utility/Primitives/BezierPath.swift +229 -143
- package/Sources/Private/Utility/Primitives/CGPointExtension.swift +35 -0
- package/{lottie-swift/src → Sources}/Private/Utility/Primitives/ColorExtension.swift +46 -14
- package/{lottie-swift/src → Sources}/Private/Utility/Primitives/CompoundBezierPath.swift +58 -49
- package/Sources/Private/Utility/Primitives/CurveVertex.swift +184 -0
- package/Sources/Private/Utility/Primitives/PathElement.swift +75 -0
- package/Sources/Private/Utility/Primitives/UnitBezier.swift +115 -0
- package/Sources/Private/Utility/Primitives/VectorsExtensions.swift +341 -0
- package/Sources/Public/Animation/AnimationPublic.swift +266 -0
- package/Sources/Public/Animation/AnimationView.swift +1335 -0
- package/Sources/Public/Animation/AnimationViewInitializers.swift +109 -0
- package/Sources/Public/AnimationCache/AnimationCacheProvider.swift +22 -0
- package/{lottie-swift/src → Sources}/Public/AnimationCache/LRUAnimationCache.swift +22 -18
- package/Sources/Public/DynamicProperties/AnimationKeypath.swift +49 -0
- package/Sources/Public/DynamicProperties/AnyValueProvider.swift +132 -0
- package/{lottie-swift/src → Sources}/Public/DynamicProperties/ValueProviders/ColorValueProvider.swift +54 -36
- package/{lottie-swift/src → Sources}/Public/DynamicProperties/ValueProviders/FloatValueProvider.swift +40 -36
- package/Sources/Public/DynamicProperties/ValueProviders/GradientValueProvider.swift +123 -0
- package/{lottie-swift/src → Sources}/Public/DynamicProperties/ValueProviders/PointValueProvider.swift +40 -35
- package/{lottie-swift/src → Sources}/Public/DynamicProperties/ValueProviders/SizeValueProvider.swift +40 -35
- package/{lottie-swift/src → Sources}/Public/FontProvider/AnimationFontProvider.swift +15 -8
- package/Sources/Public/ImageProvider/AnimationImageProvider.swift +21 -0
- package/Sources/Public/Keyframes/Interpolatable.swift +253 -0
- package/Sources/Public/Keyframes/Keyframe.swift +92 -0
- package/Sources/Public/Logging/LottieLogger.swift +137 -0
- package/Sources/Public/LottieConfiguration.swift +143 -0
- package/{lottie-swift/src → Sources}/Public/Primitives/AnimationTime.swift +1 -1
- package/{lottie-swift/src → Sources}/Public/Primitives/Color.swift +10 -6
- package/{lottie-swift/src → Sources}/Public/Primitives/Vectors.swift +13 -12
- package/Sources/Public/TextProvider/AnimationTextProvider.swift +53 -0
- package/{lottie-swift/src → Sources}/Public/iOS/AnimatedButton.swift +42 -26
- package/{lottie-swift/src → Sources}/Public/iOS/AnimatedControl.swift +100 -91
- package/{lottie-swift/src → Sources}/Public/iOS/AnimatedSwitch.swift +128 -94
- package/{lottie-swift/src → Sources}/Public/iOS/AnimationSubview.swift +3 -3
- package/Sources/Public/iOS/AnimationViewBase.swift +78 -0
- package/{lottie-swift/src → Sources}/Public/iOS/BundleImageProvider.swift +36 -34
- package/{lottie-swift/src → Sources}/Public/iOS/Compatibility/CompatibleAnimationKeypath.swift +4 -0
- package/{lottie-swift/src → Sources}/Public/iOS/Compatibility/CompatibleAnimationView.swift +38 -29
- package/{lottie-swift/src → Sources}/Public/iOS/FilepathImageProvider.swift +24 -21
- package/{lottie-swift/src → Sources}/Public/iOS/UIColorExtension.swift +4 -4
- package/{lottie-swift/src/Public/MacOS/AnimationSubview.swift → Sources/Public/macOS/AnimationSubview.macOS.swift} +4 -5
- package/{lottie-swift/src/Public/MacOS/LottieView.swift → Sources/Public/macOS/AnimationViewBase.macOS.swift} +58 -50
- package/{lottie-swift/src/Public/MacOS/BundleImageProvider.swift → Sources/Public/macOS/BundleImageProvider.macOS.swift} +31 -32
- package/Sources/Public/macOS/FilepathImageProvider.macOS.swift +67 -0
- package/Tests/AnimationKeypathTests.swift +103 -0
- package/Tests/AutomaticEngineTests.swift +57 -0
- package/Tests/BundleTests.swift +25 -0
- package/Tests/DataURLTests.swift +64 -0
- package/Tests/ParsingTests.swift +43 -0
- package/Tests/PerformanceTests.swift +214 -0
- package/Tests/Samples/9squares_AlBoardman.json +1 -0
- package/Tests/Samples/Boat_Loader.json +1 -0
- package/Tests/Samples/HamburgerArrow.json +1 -0
- package/Tests/Samples/IconTransitions.json +1 -0
- package/Tests/Samples/Images/dog.png +0 -0
- package/Tests/Samples/Issues/issue_1125.json +1 -0
- package/Tests/Samples/Issues/issue_1260.json +1 -0
- package/Tests/Samples/Issues/issue_1403.json +1 -0
- package/Tests/Samples/Issues/issue_1407.json +1 -0
- package/Tests/Samples/Issues/issue_1460.json +1 -0
- package/Tests/Samples/Issues/issue_1488.json +1 -0
- package/Tests/Samples/Issues/issue_1505.json +1 -0
- package/Tests/Samples/Issues/issue_1541.json +1 -0
- package/Tests/Samples/Issues/issue_1557.json +1 -0
- package/Tests/Samples/Issues/issue_1603.json +1 -0
- package/Tests/Samples/Issues/issue_1628.json +1 -0
- package/Tests/Samples/Issues/issue_1636.json +1 -0
- package/Tests/Samples/Issues/issue_1643.json +1 -0
- package/Tests/Samples/Issues/issue_1655.json +1 -0
- package/Tests/Samples/Issues/issue_1664.json +1 -0
- package/Tests/Samples/Issues/issue_1683.json +1 -0
- package/Tests/Samples/Issues/issue_1687.json +1 -0
- package/Tests/Samples/Issues/issue_1711.json +1 -0
- package/Tests/Samples/Issues/issue_1717.json +1 -0
- package/Tests/Samples/Issues/issue_769.json +1 -0
- package/Tests/Samples/Issues/issue_885.json +1 -0
- package/Tests/Samples/Issues/issue_965.json +1 -0
- package/Tests/Samples/Issues/pr_1536.json +1 -0
- package/Tests/Samples/Issues/pr_1563.json +8439 -0
- package/Tests/Samples/Issues/pr_1592.json +5527 -0
- package/Tests/Samples/Issues/pr_1599.json +738 -0
- package/Tests/Samples/Issues/pr_1604_1.json +1 -0
- package/Tests/Samples/Issues/pr_1604_2.json +1 -0
- package/Tests/Samples/Issues/pr_1632_1.json +1 -0
- package/Tests/Samples/Issues/pr_1632_2.json +1 -0
- package/Tests/Samples/Issues/pr_1686.json +513 -0
- package/Tests/Samples/Issues/pr_1698.json +1 -0
- package/Tests/Samples/Issues/pr_1699.json +1 -0
- package/Tests/Samples/LottieFiles/LICENSE.md +14 -0
- package/Tests/Samples/LottieFiles/bounce_strokes.json +1 -0
- package/Tests/Samples/LottieFiles/cactus.json +1 -0
- package/Tests/Samples/LottieFiles/dog_car_ride.json +1 -0
- package/Tests/Samples/LottieFiles/draft_icon.json +1 -0
- package/Tests/Samples/LottieFiles/fireworks.json +1 -0
- package/Tests/Samples/LottieFiles/gradient_1.json +1 -0
- package/Tests/Samples/LottieFiles/gradient_2.json +1 -0
- package/Tests/Samples/LottieFiles/gradient_pill.json +1 -0
- package/Tests/Samples/LottieFiles/gradient_shapes.json +1 -0
- package/Tests/Samples/LottieFiles/gradient_square.json +1 -0
- package/Tests/Samples/LottieFiles/growth.json +1 -0
- package/Tests/Samples/LottieFiles/infinity_loader.json +1 -0
- package/Tests/Samples/LottieFiles/loading_dots_1.json +1 -0
- package/Tests/Samples/LottieFiles/loading_dots_2.json +1 -0
- package/Tests/Samples/LottieFiles/loading_dots_3.json +1 -0
- package/Tests/Samples/LottieFiles/loading_gradient_strokes.json +1 -0
- package/Tests/Samples/LottieFiles/settings_slider.json +1 -0
- package/Tests/Samples/LottieFiles/shop.json +1 -0
- package/Tests/Samples/LottieFiles/step_loader.json +1 -0
- package/Tests/Samples/LottieLogo1.json +1 -0
- package/Tests/Samples/LottieLogo1_masked.json +1 -0
- package/Tests/Samples/LottieLogo2.json +1 -0
- package/Tests/Samples/MotionCorpse_Jrcanest.json +1 -0
- package/Tests/Samples/Nonanimating/BasicLayers.json +1 -0
- package/Tests/Samples/Nonanimating/DisableNodesTest.json +1 -0
- package/Tests/Samples/Nonanimating/FirstText.json +1 -0
- package/Tests/Samples/Nonanimating/GeometryTransformTest.json +1 -0
- package/Tests/Samples/Nonanimating/Text_AnimatedProperties.json +1 -0
- package/Tests/Samples/Nonanimating/Text_Glyph.json +1 -0
- package/Tests/Samples/Nonanimating/Text_NoAnimation.json +1 -0
- package/Tests/Samples/Nonanimating/Text_NoGlyph.json +1 -0
- package/Tests/Samples/Nonanimating/Zoom.json +1 -0
- package/Tests/Samples/Nonanimating/_dog.json +1 -0
- package/Tests/Samples/Nonanimating/base64Test.json +1 -0
- package/Tests/Samples/Nonanimating/blend_mode_test.json +1 -0
- package/Tests/Samples/Nonanimating/keypathTest.json +1 -0
- package/Tests/Samples/Nonanimating/verifyLineHeight.json +1 -0
- package/Tests/Samples/PinJump.json +1 -0
- package/Tests/Samples/Switch.json +1 -0
- package/Tests/Samples/Switch_States.json +1 -0
- package/Tests/Samples/TwitterHeart.json +1 -0
- package/Tests/Samples/TwitterHeartButton.json +1 -0
- package/Tests/Samples/TypeFace/A.json +1 -0
- package/Tests/Samples/TypeFace/Apostrophe.json +1 -0
- package/Tests/Samples/TypeFace/B.json +1 -0
- package/Tests/Samples/TypeFace/BlinkingCursor.json +1 -0
- package/Tests/Samples/TypeFace/C.json +1 -0
- package/Tests/Samples/TypeFace/Colon.json +1 -0
- package/Tests/Samples/TypeFace/Comma.json +1 -0
- package/Tests/Samples/TypeFace/D.json +1 -0
- package/Tests/Samples/TypeFace/E.json +1 -0
- package/Tests/Samples/TypeFace/F.json +1 -0
- package/Tests/Samples/TypeFace/G.json +1 -0
- package/Tests/Samples/TypeFace/H.json +1 -0
- package/Tests/Samples/TypeFace/I.json +1 -0
- package/Tests/Samples/TypeFace/J.json +1 -0
- package/Tests/Samples/TypeFace/K.json +1 -0
- package/Tests/Samples/TypeFace/L.json +1 -0
- package/Tests/Samples/TypeFace/M.json +1 -0
- package/Tests/Samples/TypeFace/N.json +1 -0
- package/Tests/Samples/TypeFace/O.json +1 -0
- package/Tests/Samples/TypeFace/P.json +1 -0
- package/Tests/Samples/TypeFace/Q.json +1 -0
- package/Tests/Samples/TypeFace/R.json +1 -0
- package/Tests/Samples/TypeFace/S.json +1 -0
- package/Tests/Samples/TypeFace/T.json +1 -0
- package/Tests/Samples/TypeFace/U.json +1 -0
- package/Tests/Samples/TypeFace/V.json +1 -0
- package/Tests/Samples/TypeFace/W.json +1 -0
- package/Tests/Samples/TypeFace/X.json +1 -0
- package/Tests/Samples/TypeFace/Y.json +1 -0
- package/Tests/Samples/TypeFace/Z.json +1 -0
- package/Tests/Samples/Watermelon.json +1 -0
- package/Tests/Samples/setValueTest.json +1 -0
- package/Tests/Samples/timeremap.json +1 -0
- package/Tests/Samples/vcTransition1.json +1 -0
- package/Tests/Samples/vcTransition2.json +1 -0
- package/Tests/SnapshotConfiguration.swift +158 -0
- package/Tests/SnapshotTests.swift +265 -0
- package/Tests/Utils/Bundle+Module.swift +30 -0
- package/Tests/Utils/HardcodedFontProvider.swift +19 -0
- package/Tests/Utils/HardcodedImageProvider.swift +23 -0
- package/Tests/Utils/Snapshotting+presentationLayer.swift +47 -0
- package/Tests/ValueProvidersTests.swift +27 -0
- package/lottie-ios.podspec +12 -12
- package/package.json +1 -1
- package/script/test-carthage/Cartfile +1 -0
- package/script/test-carthage/Cartfile.resolved +1 -0
- package/script/test-carthage/CarthageTest/AppDelegate.swift +26 -0
- package/script/test-carthage/CarthageTest/Assets.xcassets/AccentColor.colorset/Contents.json +11 -0
- package/script/test-carthage/CarthageTest/Assets.xcassets/AppIcon.appiconset/Contents.json +98 -0
- package/script/test-carthage/CarthageTest/Assets.xcassets/Contents.json +6 -0
- package/script/test-carthage/CarthageTest/Base.lproj/LaunchScreen.storyboard +25 -0
- package/script/test-carthage/CarthageTest/Base.lproj/Main.storyboard +24 -0
- package/script/test-carthage/CarthageTest/Info.plist +66 -0
- package/script/test-carthage/CarthageTest/SceneDelegate.swift +10 -0
- package/script/test-carthage/CarthageTest/ViewController.swift +15 -0
- package/script/test-carthage/CarthageTest-macOS/AppDelegate.swift +7 -0
- package/script/test-carthage/CarthageTest-macOS/Assets.xcassets/AccentColor.colorset/Contents.json +11 -0
- package/script/test-carthage/CarthageTest-macOS/Assets.xcassets/AppIcon.appiconset/Contents.json +58 -0
- package/script/test-carthage/CarthageTest-macOS/Assets.xcassets/Contents.json +6 -0
- package/script/test-carthage/CarthageTest-macOS/Base.lproj/Main.storyboard +717 -0
- package/script/test-carthage/CarthageTest-macOS/CarthageTest_macOS.entitlements +10 -0
- package/script/test-carthage/CarthageTest-macOS/ViewController.swift +15 -0
- package/script/test-carthage/CarthageTest.xcodeproj/project.pbxproj +532 -0
- package/script/test-carthage/CarthageTest.xcodeproj/project.xcworkspace/contents.xcworkspacedata +7 -0
- package/script/test-carthage/CarthageTest.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist +8 -0
- package/script/test-carthage/CarthageTest.xcodeproj/project.xcworkspace/xcuserdata/cal.xcuserdatad/UserInterfaceState.xcuserstate +0 -0
- package/script/test-carthage/CarthageTest.xcodeproj/project.xcworkspace/xcuserdata/calstephens.xcuserdatad/UserInterfaceState.xcuserstate +0 -0
- package/script/test-carthage/CarthageTest.xcodeproj/xcuserdata/cal.xcuserdatad/xcschemes/xcschememanagement.plist +14 -0
- package/script/test-carthage/CarthageTest.xcodeproj/xcuserdata/calstephens.xcuserdatad/xcschemes/xcschememanagement.plist +19 -0
- package/script/test-carthage/Mintfile +1 -0
- package/script/test-spm/LottieSPM.xcworkspace/contents.xcworkspacedata +7 -0
- package/script/test-spm/LottieSPM.xcworkspace/xcuserdata/cal.xcuserdatad/UserInterfaceState.xcuserstate +0 -0
- package/script/test-spm/Mintfile +1 -0
- package/.swiftpm/xcode/package.xcworkspace/xcuserdata/brandonwithrow.xcuserdatad/UserInterfaceState.xcuserstate +0 -0
- package/Lottie/Info.plist +0 -22
- package/Lottie.xcodeproj/project.xcworkspace/xcuserdata/brandonwithrow.xcuserdatad/UserInterfaceState.xcuserstate +0 -0
- package/Lottie.xcodeproj/xcuserdata/brandonwithrow.xcuserdatad/xcschemes/LottieLibraryMacOS.xcscheme +0 -80
- package/Lottie.xcodeproj/xcuserdata/brandonwithrow.xcuserdatad/xcschemes/xcschememanagement.plist +0 -57
- package/lottie-swift/Assets/.gitkeep +0 -0
- package/lottie-swift/src/Private/LayerContainers/Utility/LayerFontProvider.swift +0 -37
- package/lottie-swift/src/Private/LayerContainers/Utility/LayerTextProvider.swift +0 -36
- package/lottie-swift/src/Private/Model/Animation.swift +0 -107
- package/lottie-swift/src/Private/Model/Assets/Asset.swift +0 -27
- package/lottie-swift/src/Private/Model/Assets/ImageAsset.swift +0 -48
- package/lottie-swift/src/Private/Model/Keyframes/Keyframe.swift +0 -128
- package/lottie-swift/src/Private/Model/Keyframes/KeyframeGroup.swift +0 -108
- package/lottie-swift/src/Private/Model/Layers/LayerModel.swift +0 -150
- package/lottie-swift/src/Private/Model/Objects/DashPattern.swift +0 -24
- package/lottie-swift/src/Private/Model/Objects/Marker.swift +0 -23
- package/lottie-swift/src/Private/Model/Objects/Mask.swift +0 -48
- package/lottie-swift/src/Private/Model/Objects/Transform.swift +0 -105
- package/lottie-swift/src/Private/Model/ShapeItems/Ellipse.swift +0 -50
- package/lottie-swift/src/Private/Model/ShapeItems/FillI.swift +0 -49
- package/lottie-swift/src/Private/Model/ShapeItems/GradientFill.swift +0 -86
- package/lottie-swift/src/Private/Model/ShapeItems/GradientStroke.swift +0 -125
- package/lottie-swift/src/Private/Model/ShapeItems/Group.swift +0 -32
- package/lottie-swift/src/Private/Model/ShapeItems/Rectangle.swift +0 -50
- package/lottie-swift/src/Private/Model/ShapeItems/Repeater.swift +0 -80
- package/lottie-swift/src/Private/Model/ShapeItems/Shape.swift +0 -37
- package/lottie-swift/src/Private/Model/ShapeItems/ShapeItem.swift +0 -95
- package/lottie-swift/src/Private/Model/ShapeItems/ShapeTransform.swift +0 -68
- package/lottie-swift/src/Private/Model/ShapeItems/Star.swift +0 -86
- package/lottie-swift/src/Private/Model/ShapeItems/Stroke.swift +0 -67
- package/lottie-swift/src/Private/Model/ShapeItems/Trim.swift +0 -53
- package/lottie-swift/src/Private/Model/Text/Font.swift +0 -35
- package/lottie-swift/src/Private/Model/Text/TextAnimator.swift +0 -99
- package/lottie-swift/src/Private/Model/Text/TextDocument.swift +0 -70
- package/lottie-swift/src/Private/NodeRenderSystem/Nodes/PathNodes/EllipseNode.swift +0 -109
- package/lottie-swift/src/Private/NodeRenderSystem/Nodes/PathNodes/PolygonNode.swift +0 -132
- package/lottie-swift/src/Private/NodeRenderSystem/Nodes/RenderContainers/GroupNode.swift +0 -141
- package/lottie-swift/src/Private/NodeRenderSystem/Nodes/RenderNodes/StrokeNode.swift +0 -127
- package/lottie-swift/src/Private/Utility/Extensions/MathKit.swift +0 -539
- package/lottie-swift/src/Private/Utility/Extensions/StringExtensions.swift +0 -32
- package/lottie-swift/src/Private/Utility/Interpolatable/Interpolatable.swift +0 -18
- package/lottie-swift/src/Private/Utility/Interpolatable/InterpolatableExtensions.swift +0 -170
- package/lottie-swift/src/Private/Utility/Primitives/CurveVertex.swift +0 -177
- package/lottie-swift/src/Private/Utility/Primitives/PathElement.swift +0 -68
- package/lottie-swift/src/Private/Utility/Primitives/VectorsExtensions.swift +0 -218
- package/lottie-swift/src/Public/Animation/AnimationPublic.swift +0 -196
- package/lottie-swift/src/Public/Animation/AnimationView.swift +0 -1006
- package/lottie-swift/src/Public/Animation/AnimationViewInitializers.swift +0 -83
- package/lottie-swift/src/Public/AnimationCache/AnimationCacheProvider.swift +0 -24
- package/lottie-swift/src/Public/DynamicProperties/AnimationKeypath.swift +0 -46
- package/lottie-swift/src/Public/DynamicProperties/AnyValueProvider.swift +0 -29
- package/lottie-swift/src/Public/DynamicProperties/ValueProviders/GradientValueProvider.swift +0 -114
- package/lottie-swift/src/Public/ImageProvider/AnimationImageProvider.swift +0 -23
- package/lottie-swift/src/Public/MacOS/FilepathImageProvider.swift +0 -67
- package/lottie-swift/src/Public/TextProvider/AnimationTextProvider.swift +0 -39
- package/lottie-swift/src/Public/iOS/LottieView.swift +0 -62
- package/lottie-swift-testing.podspec +0 -32
|
@@ -0,0 +1,154 @@
|
|
|
1
|
+
// Created by Cal Stephens on 1/28/22.
|
|
2
|
+
// Copyright © 2022 Airbnb Inc. All rights reserved.
|
|
3
|
+
|
|
4
|
+
// MARK: - Keyframes
|
|
5
|
+
|
|
6
|
+
enum Keyframes {
|
|
7
|
+
|
|
8
|
+
// MARK: Internal
|
|
9
|
+
|
|
10
|
+
/// Combines the given keyframe groups of `Keyframe<T>`s into a single keyframe group of
|
|
11
|
+
/// of `Keyframe<[T]>`s if all of the `KeyframeGroup`s have the exact same animation timing
|
|
12
|
+
static func combinedIfPossible<T>(_ allGroups: [KeyframeGroup<T>]) -> KeyframeGroup<[T]>? {
|
|
13
|
+
combinedIfPossible(allGroups, makeCombinedResult: { index in
|
|
14
|
+
allGroups.map { $0.valueForCombinedKeyframes(at: index) }
|
|
15
|
+
})
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
/// Combines the given keyframe groups of `Keyframe<T>`s into a single keyframe group of
|
|
19
|
+
/// of `Keyframe<[T]>`s if all of the `KeyframeGroup`s have the exact same animation timing
|
|
20
|
+
static func combinedIfPossible<T1, T2, CombinedResult>(
|
|
21
|
+
_ k1: KeyframeGroup<T1>,
|
|
22
|
+
_ k2: KeyframeGroup<T2>,
|
|
23
|
+
makeCombinedResult: (T1, T2) -> CombinedResult)
|
|
24
|
+
-> KeyframeGroup<CombinedResult>?
|
|
25
|
+
{
|
|
26
|
+
combinedIfPossible(
|
|
27
|
+
[k1, k2],
|
|
28
|
+
makeCombinedResult: { index in
|
|
29
|
+
makeCombinedResult(
|
|
30
|
+
k1.valueForCombinedKeyframes(at: index),
|
|
31
|
+
k2.valueForCombinedKeyframes(at: index))
|
|
32
|
+
})
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
/// Combines the given keyframe groups of `Keyframe<T>`s into a single keyframe group of
|
|
36
|
+
/// of `Keyframe<[T]>`s if all of the `KeyframeGroup`s have the exact same animation timing
|
|
37
|
+
static func combinedIfPossible<T1, T2, T3, CombinedResult>(
|
|
38
|
+
_ k1: KeyframeGroup<T1>,
|
|
39
|
+
_ k2: KeyframeGroup<T2>,
|
|
40
|
+
_ k3: KeyframeGroup<T3>,
|
|
41
|
+
makeCombinedResult: (T1, T2, T3) -> CombinedResult)
|
|
42
|
+
-> KeyframeGroup<CombinedResult>?
|
|
43
|
+
{
|
|
44
|
+
combinedIfPossible(
|
|
45
|
+
[k1, k2, k3],
|
|
46
|
+
makeCombinedResult: { index in
|
|
47
|
+
makeCombinedResult(
|
|
48
|
+
k1.valueForCombinedKeyframes(at: index),
|
|
49
|
+
k2.valueForCombinedKeyframes(at: index),
|
|
50
|
+
k3.valueForCombinedKeyframes(at: index))
|
|
51
|
+
})
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
/// Combines the given keyframe groups of `Keyframe<T>`s into a single keyframe group of
|
|
55
|
+
/// of `Keyframe<[T]>`s if all of the `KeyframeGroup`s have the exact same animation timing
|
|
56
|
+
static func combinedIfPossible<T1, T2, T3, T4, T5, T6, T7, CombinedResult>(
|
|
57
|
+
_ k1: KeyframeGroup<T1>,
|
|
58
|
+
_ k2: KeyframeGroup<T2>,
|
|
59
|
+
_ k3: KeyframeGroup<T3>,
|
|
60
|
+
_ k4: KeyframeGroup<T4>,
|
|
61
|
+
_ k5: KeyframeGroup<T5>,
|
|
62
|
+
_ k6: KeyframeGroup<T6>,
|
|
63
|
+
_ k7: KeyframeGroup<T7>,
|
|
64
|
+
makeCombinedResult: (T1, T2, T3, T4, T5, T6, T7) -> CombinedResult)
|
|
65
|
+
-> KeyframeGroup<CombinedResult>?
|
|
66
|
+
{
|
|
67
|
+
combinedIfPossible(
|
|
68
|
+
[k1, k2, k3, k4, k5, k6, k7],
|
|
69
|
+
makeCombinedResult: { index in
|
|
70
|
+
makeCombinedResult(
|
|
71
|
+
k1.valueForCombinedKeyframes(at: index),
|
|
72
|
+
k2.valueForCombinedKeyframes(at: index),
|
|
73
|
+
k3.valueForCombinedKeyframes(at: index),
|
|
74
|
+
k4.valueForCombinedKeyframes(at: index),
|
|
75
|
+
k5.valueForCombinedKeyframes(at: index),
|
|
76
|
+
k6.valueForCombinedKeyframes(at: index),
|
|
77
|
+
k7.valueForCombinedKeyframes(at: index))
|
|
78
|
+
})
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
// MARK: Private
|
|
82
|
+
|
|
83
|
+
/// Combines the given `[KeyframeGroup]` of `Keyframe<T>`s into a single `KeyframeGroup`
|
|
84
|
+
/// of `Keyframe<CombinedResult>`s if all of the `KeyframeGroup`s have the exact same animation timing
|
|
85
|
+
private static func combinedIfPossible<CombinedResult>(
|
|
86
|
+
_ allGroups: [AnyKeyframeGroup],
|
|
87
|
+
makeCombinedResult: (_ index: Int) -> CombinedResult)
|
|
88
|
+
-> KeyframeGroup<CombinedResult>?
|
|
89
|
+
{
|
|
90
|
+
let untypedGroups = allGroups.map { $0.untyped }
|
|
91
|
+
|
|
92
|
+
// Animations with no timing information (e.g. with just a single keyframe)
|
|
93
|
+
// can be trivially combined with any other set of keyframes, so we don't need
|
|
94
|
+
// to check those.
|
|
95
|
+
let animatingKeyframes = untypedGroups.filter { $0.keyframes.count > 1 }
|
|
96
|
+
|
|
97
|
+
guard
|
|
98
|
+
!allGroups.isEmpty,
|
|
99
|
+
animatingKeyframes.allSatisfy({ $0.hasSameTimingParameters(as: animatingKeyframes[0]) })
|
|
100
|
+
else { return nil }
|
|
101
|
+
|
|
102
|
+
var combinedKeyframes = ContiguousArray<Keyframe<CombinedResult>>()
|
|
103
|
+
let baseKeyframes = (animatingKeyframes.first ?? untypedGroups[0]).keyframes
|
|
104
|
+
|
|
105
|
+
for index in baseKeyframes.indices {
|
|
106
|
+
let baseKeyframe = baseKeyframes[index]
|
|
107
|
+
let combinedValue = makeCombinedResult(index)
|
|
108
|
+
combinedKeyframes.append(baseKeyframe.withValue(combinedValue))
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
return KeyframeGroup(keyframes: combinedKeyframes)
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
extension KeyframeGroup {
|
|
117
|
+
/// Whether or not all of the keyframes in this `KeyframeGroup` have the same
|
|
118
|
+
/// timing parameters as the corresponding keyframe in the other given `KeyframeGroup`
|
|
119
|
+
func hasSameTimingParameters<T>(as other: KeyframeGroup<T>) -> Bool {
|
|
120
|
+
guard keyframes.count == other.keyframes.count else {
|
|
121
|
+
return false
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
return zip(keyframes, other.keyframes).allSatisfy {
|
|
125
|
+
$0.hasSameTimingParameters(as: $1)
|
|
126
|
+
}
|
|
127
|
+
}
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
extension Keyframe {
|
|
131
|
+
/// Whether or not this keyframe has the same timing parameters as the given keyframe,
|
|
132
|
+
/// excluding `spatialInTangent` and `spatialOutTangent`.
|
|
133
|
+
fileprivate func hasSameTimingParameters<T>(as other: Keyframe<T>) -> Bool {
|
|
134
|
+
time == other.time
|
|
135
|
+
&& isHold == other.isHold
|
|
136
|
+
&& inTangent == other.inTangent
|
|
137
|
+
&& outTangent == other.outTangent
|
|
138
|
+
// We intentionally don't compare spatial in/out tangents,
|
|
139
|
+
// since those values are only used in very specific cases
|
|
140
|
+
// (animating the x/y position of a layer), which aren't ever
|
|
141
|
+
// combined in this way.
|
|
142
|
+
}
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
extension KeyframeGroup {
|
|
146
|
+
/// The value to use for a combined set of keyframes, for the given index
|
|
147
|
+
fileprivate func valueForCombinedKeyframes(at index: Int) -> T {
|
|
148
|
+
if keyframes.count == 1 {
|
|
149
|
+
return keyframes[0].value
|
|
150
|
+
} else {
|
|
151
|
+
return keyframes[index].value
|
|
152
|
+
}
|
|
153
|
+
}
|
|
154
|
+
}
|
|
@@ -0,0 +1,83 @@
|
|
|
1
|
+
// Created by Cal Stephens on 12/14/21.
|
|
2
|
+
// Copyright © 2021 Airbnb Inc. All rights reserved.
|
|
3
|
+
|
|
4
|
+
import QuartzCore
|
|
5
|
+
|
|
6
|
+
// MARK: - AnimationLayer
|
|
7
|
+
|
|
8
|
+
/// A type of `CALayer` that can be used in a Lottie animation
|
|
9
|
+
/// - Layers backed by a `LayerModel` subclass should subclass `BaseCompositionLayer`
|
|
10
|
+
protocol AnimationLayer: CALayer {
|
|
11
|
+
/// Instructs this layer to setup its `CAAnimation`s
|
|
12
|
+
/// using the given `LayerAnimationContext`
|
|
13
|
+
func setupAnimations(context: LayerAnimationContext) throws
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
// MARK: - LayerAnimationContext
|
|
17
|
+
|
|
18
|
+
// Context describing the timing parameters of the current animation
|
|
19
|
+
struct LayerAnimationContext {
|
|
20
|
+
/// The animation being played
|
|
21
|
+
let animation: Animation
|
|
22
|
+
|
|
23
|
+
/// The timing configuration that should be applied to `CAAnimation`s
|
|
24
|
+
let timingConfiguration: CoreAnimationLayer.CAMediaTimingConfiguration
|
|
25
|
+
|
|
26
|
+
/// The absolute frame number that this animation begins at
|
|
27
|
+
let startFrame: AnimationFrameTime
|
|
28
|
+
|
|
29
|
+
/// The absolute frame number that this animation ends at
|
|
30
|
+
let endFrame: AnimationFrameTime
|
|
31
|
+
|
|
32
|
+
/// The set of custom Value Providers applied to this animation
|
|
33
|
+
let valueProviderStore: ValueProviderStore
|
|
34
|
+
|
|
35
|
+
/// Information about whether or not an animation is compatible with the Core Animation engine
|
|
36
|
+
let compatibilityTracker: CompatibilityTracker
|
|
37
|
+
|
|
38
|
+
/// The logger that should be used for assertions and warnings
|
|
39
|
+
let logger: LottieLogger
|
|
40
|
+
|
|
41
|
+
/// The AnimationKeypath represented by the current layer
|
|
42
|
+
var currentKeypath: AnimationKeypath
|
|
43
|
+
|
|
44
|
+
/// Whether or not to log `AnimationKeypath`s for all of the animation's layers
|
|
45
|
+
/// - Used for `CoreAnimationLayer.logHierarchyKeypaths()`
|
|
46
|
+
var logHierarchyKeypaths: Bool
|
|
47
|
+
|
|
48
|
+
/// A closure that remaps the given frame in the child layer's local time to a frame
|
|
49
|
+
/// in the animation's overall global time
|
|
50
|
+
private(set) var timeRemapping: ((AnimationFrameTime) -> AnimationFrameTime) = { $0 }
|
|
51
|
+
|
|
52
|
+
/// Adds the given component string to the `AnimationKeypath` stored
|
|
53
|
+
/// that describes the current path being configured by this context value
|
|
54
|
+
func addingKeypathComponent(_ component: String) -> LayerAnimationContext {
|
|
55
|
+
var context = self
|
|
56
|
+
context.currentKeypath.keys.append(component)
|
|
57
|
+
return context
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
/// The `AnimationProgressTime` for the given `AnimationFrameTime` within this layer,
|
|
61
|
+
/// accounting for the `timeRemapping` applied to this layer
|
|
62
|
+
func progressTime(for frame: AnimationFrameTime) -> AnimationProgressTime {
|
|
63
|
+
animation.progressTime(forFrame: timeRemapping(frame), clamped: false)
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
/// The real-time `TimeInterval` for the given `AnimationFrameTime` within this layer,
|
|
67
|
+
/// accounting for the `timeRemapping` applied to this layer
|
|
68
|
+
func time(for frame: AnimationFrameTime) -> TimeInterval {
|
|
69
|
+
animation.time(forFrame: timeRemapping(frame))
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
/// Chains an additional `timeRemapping` closure onto this layer context
|
|
73
|
+
func withTimeRemapping(
|
|
74
|
+
_ additionalTimeRemapping: @escaping (AnimationFrameTime) -> AnimationFrameTime)
|
|
75
|
+
-> LayerAnimationContext
|
|
76
|
+
{
|
|
77
|
+
var copy = self
|
|
78
|
+
copy.timeRemapping = { [existingTimeRemapping = timeRemapping] time in
|
|
79
|
+
existingTimeRemapping(additionalTimeRemapping(time))
|
|
80
|
+
}
|
|
81
|
+
return copy
|
|
82
|
+
}
|
|
83
|
+
}
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
// Created by Cal Stephens on 1/27/22.
|
|
2
|
+
// Copyright © 2022 Airbnb Inc. All rights reserved.
|
|
3
|
+
|
|
4
|
+
import QuartzCore
|
|
5
|
+
|
|
6
|
+
/// A base `CALayer` that manages the frame and animations
|
|
7
|
+
/// of its `sublayers` and `mask`
|
|
8
|
+
class BaseAnimationLayer: CALayer, AnimationLayer {
|
|
9
|
+
|
|
10
|
+
// MARK: Internal
|
|
11
|
+
|
|
12
|
+
override func layoutSublayers() {
|
|
13
|
+
super.layoutSublayers()
|
|
14
|
+
|
|
15
|
+
for sublayer in managedSublayers {
|
|
16
|
+
sublayer.fillBoundsOfSuperlayer()
|
|
17
|
+
}
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
func setupAnimations(context: LayerAnimationContext) throws {
|
|
21
|
+
for childAnimationLayer in managedSublayers {
|
|
22
|
+
try (childAnimationLayer as? AnimationLayer)?.setupAnimations(context: context)
|
|
23
|
+
}
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
// MARK: Private
|
|
27
|
+
|
|
28
|
+
/// All of the sublayers managed by this container
|
|
29
|
+
private var managedSublayers: [CALayer] {
|
|
30
|
+
(sublayers ?? []) + [mask].compactMap { $0 }
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
}
|
|
@@ -0,0 +1,88 @@
|
|
|
1
|
+
// Created by Cal Stephens on 12/20/21.
|
|
2
|
+
// Copyright © 2021 Airbnb Inc. All rights reserved.
|
|
3
|
+
|
|
4
|
+
import QuartzCore
|
|
5
|
+
|
|
6
|
+
// MARK: - BaseCompositionLayer
|
|
7
|
+
|
|
8
|
+
/// The base type of `AnimationLayer` that can contain other `AnimationLayer`s
|
|
9
|
+
class BaseCompositionLayer: BaseAnimationLayer {
|
|
10
|
+
|
|
11
|
+
// MARK: Lifecycle
|
|
12
|
+
|
|
13
|
+
init(layerModel: LayerModel) {
|
|
14
|
+
baseLayerModel = layerModel
|
|
15
|
+
super.init()
|
|
16
|
+
|
|
17
|
+
setupSublayers()
|
|
18
|
+
compositingFilter = layerModel.blendMode.filterName
|
|
19
|
+
name = layerModel.name
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
required init?(coder _: NSCoder) {
|
|
23
|
+
fatalError("init(coder:) has not been implemented")
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
/// Called by CoreAnimation to create a shadow copy of this layer
|
|
27
|
+
/// More details: https://developer.apple.com/documentation/quartzcore/calayer/1410842-init
|
|
28
|
+
override init(layer: Any) {
|
|
29
|
+
guard let typedLayer = layer as? Self else {
|
|
30
|
+
fatalError("\(Self.self).init(layer:) incorrectly called with \(type(of: layer))")
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
baseLayerModel = typedLayer.baseLayerModel
|
|
34
|
+
super.init(layer: typedLayer)
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
// MARK: Internal
|
|
38
|
+
|
|
39
|
+
/// Whether or not this layer render should render any visible content
|
|
40
|
+
var renderLayerContents: Bool { true }
|
|
41
|
+
|
|
42
|
+
/// Sets up the base `LayerModel` animations for this layer,
|
|
43
|
+
/// and all child `AnimationLayer`s.
|
|
44
|
+
/// - Can be overridden by subclasses, which much call `super`.
|
|
45
|
+
override func setupAnimations(context: LayerAnimationContext) throws {
|
|
46
|
+
var context = context
|
|
47
|
+
if renderLayerContents {
|
|
48
|
+
context = context.addingKeypathComponent(baseLayerModel.name)
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
try setupLayerAnimations(context: context)
|
|
52
|
+
try setupChildAnimations(context: context)
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
func setupLayerAnimations(context: LayerAnimationContext) throws {
|
|
56
|
+
let context = context.addingKeypathComponent(baseLayerModel.name)
|
|
57
|
+
|
|
58
|
+
try addTransformAnimations(for: baseLayerModel.transform, context: context)
|
|
59
|
+
|
|
60
|
+
if renderLayerContents {
|
|
61
|
+
try addOpacityAnimation(for: baseLayerModel.transform, context: context)
|
|
62
|
+
|
|
63
|
+
addVisibilityAnimation(
|
|
64
|
+
inFrame: CGFloat(baseLayerModel.inFrame),
|
|
65
|
+
outFrame: CGFloat(baseLayerModel.outFrame),
|
|
66
|
+
context: context)
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
func setupChildAnimations(context: LayerAnimationContext) throws {
|
|
71
|
+
try super.setupAnimations(context: context)
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
// MARK: Private
|
|
75
|
+
|
|
76
|
+
private let baseLayerModel: LayerModel
|
|
77
|
+
|
|
78
|
+
private func setupSublayers() {
|
|
79
|
+
if
|
|
80
|
+
renderLayerContents,
|
|
81
|
+
let masks = baseLayerModel.masks?.filter({ $0.mode != .none }),
|
|
82
|
+
!masks.isEmpty
|
|
83
|
+
{
|
|
84
|
+
mask = MaskCompositionLayer(masks: masks)
|
|
85
|
+
}
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
}
|
|
@@ -0,0 +1,167 @@
|
|
|
1
|
+
// Created by Cal Stephens on 1/11/22.
|
|
2
|
+
// Copyright © 2022 Airbnb Inc. All rights reserved.
|
|
3
|
+
|
|
4
|
+
import QuartzCore
|
|
5
|
+
|
|
6
|
+
extension CALayer {
|
|
7
|
+
|
|
8
|
+
// MARK: Internal
|
|
9
|
+
|
|
10
|
+
/// Sets up an `AnimationLayer` / `CALayer` hierarchy in this layer,
|
|
11
|
+
/// using the given list of layers.
|
|
12
|
+
@nonobjc
|
|
13
|
+
func setupLayerHierarchy(
|
|
14
|
+
for layers: [LayerModel],
|
|
15
|
+
context: LayerContext)
|
|
16
|
+
throws
|
|
17
|
+
{
|
|
18
|
+
// An `Animation`'s `LayerModel`s are listed from front to back,
|
|
19
|
+
// but `CALayer.sublayers` are listed from back to front.
|
|
20
|
+
// We reverse the layer ordering to match what Core Animation expects.
|
|
21
|
+
// The final view hierarchy must display the layers in this exact order.
|
|
22
|
+
let layersInZAxisOrder = layers.reversed()
|
|
23
|
+
|
|
24
|
+
let layersByIndex = Dictionary(grouping: layersInZAxisOrder, by: \.index)
|
|
25
|
+
.compactMapValues(\.first)
|
|
26
|
+
|
|
27
|
+
/// Layers specify a `parent` layer. Child layers inherit the `transform` of their parent.
|
|
28
|
+
/// - We can't add the child as a sublayer of the parent `CALayer`, since that would
|
|
29
|
+
/// break the ordering specified in `layersInZAxisOrder`.
|
|
30
|
+
/// - Instead, we create an invisible `TransformLayer` to handle the parent
|
|
31
|
+
/// transform animations, and add the child layer to that `TransformLayer`.
|
|
32
|
+
func makeParentTransformLayer(
|
|
33
|
+
childLayerModel: LayerModel,
|
|
34
|
+
childLayer: CALayer,
|
|
35
|
+
name: (LayerModel) -> String)
|
|
36
|
+
-> CALayer
|
|
37
|
+
{
|
|
38
|
+
guard
|
|
39
|
+
let parentIndex = childLayerModel.parent,
|
|
40
|
+
let parentLayerModel = layersByIndex[parentIndex]
|
|
41
|
+
else { return childLayer }
|
|
42
|
+
|
|
43
|
+
let parentLayer = TransformLayer(layerModel: parentLayerModel)
|
|
44
|
+
parentLayer.name = name(parentLayerModel)
|
|
45
|
+
parentLayer.addSublayer(childLayer)
|
|
46
|
+
|
|
47
|
+
return makeParentTransformLayer(
|
|
48
|
+
childLayerModel: parentLayerModel,
|
|
49
|
+
childLayer: parentLayer,
|
|
50
|
+
name: name)
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
// Create an `AnimationLayer` for each `LayerModel`
|
|
54
|
+
for (layerModel, mask) in try layersInZAxisOrder.pairedLayersAndMasks(context: context) {
|
|
55
|
+
guard let layer = try layerModel.makeAnimationLayer(context: context) else {
|
|
56
|
+
continue
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
// If this layer has a `parent`, we create an invisible `TransformLayer`
|
|
60
|
+
// to handle displaying / animating the parent transform.
|
|
61
|
+
let parentTransformLayer = makeParentTransformLayer(
|
|
62
|
+
childLayerModel: layerModel,
|
|
63
|
+
childLayer: layer,
|
|
64
|
+
name: { parentLayerModel in
|
|
65
|
+
"\(layerModel.name) (parent, \(parentLayerModel.name))"
|
|
66
|
+
})
|
|
67
|
+
|
|
68
|
+
// Create the `mask` layer for this layer, if it has a `MatteType`
|
|
69
|
+
if
|
|
70
|
+
let mask = mask,
|
|
71
|
+
let maskLayer = try maskLayer(for: mask.model, type: mask.matteType, context: context)
|
|
72
|
+
{
|
|
73
|
+
let maskParentTransformLayer = makeParentTransformLayer(
|
|
74
|
+
childLayerModel: mask.model,
|
|
75
|
+
childLayer: maskLayer,
|
|
76
|
+
name: { parentLayerModel in
|
|
77
|
+
"\(mask.model.name) (mask of \(layerModel.name)) (parent, \(parentLayerModel.name))"
|
|
78
|
+
})
|
|
79
|
+
|
|
80
|
+
// Set up a parent container to host both the layer
|
|
81
|
+
// and its mask in the same coordinate space
|
|
82
|
+
let maskContainer = BaseAnimationLayer()
|
|
83
|
+
maskContainer.name = "\(layerModel.name) (parent, masked)"
|
|
84
|
+
maskContainer.addSublayer(parentTransformLayer)
|
|
85
|
+
|
|
86
|
+
// Core Animation will silently fail to apply a mask if a `mask` layer
|
|
87
|
+
// itself _also_ has a `mask`. As a workaround, we can wrap this layer's
|
|
88
|
+
// mask in an additional container layer which never has its own `mask`.
|
|
89
|
+
let additionalMaskParent = BaseAnimationLayer()
|
|
90
|
+
additionalMaskParent.addSublayer(maskParentTransformLayer)
|
|
91
|
+
maskContainer.mask = additionalMaskParent
|
|
92
|
+
|
|
93
|
+
addSublayer(maskContainer)
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
else {
|
|
97
|
+
addSublayer(parentTransformLayer)
|
|
98
|
+
}
|
|
99
|
+
}
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
// MARK: Fileprivate
|
|
103
|
+
|
|
104
|
+
/// Creates a mask `CALayer` from the given matte layer model, using the `MatteType`
|
|
105
|
+
/// from the layer that is being masked.
|
|
106
|
+
fileprivate func maskLayer(
|
|
107
|
+
for matteLayerModel: LayerModel,
|
|
108
|
+
type: MatteType,
|
|
109
|
+
context: LayerContext) throws
|
|
110
|
+
-> CALayer?
|
|
111
|
+
{
|
|
112
|
+
switch type {
|
|
113
|
+
case .add:
|
|
114
|
+
return try matteLayerModel.makeAnimationLayer(context: context)
|
|
115
|
+
|
|
116
|
+
case .invert:
|
|
117
|
+
guard let maskLayer = try matteLayerModel.makeAnimationLayer(context: context) else {
|
|
118
|
+
return nil
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
// We can invert the mask layer by having a large solid black layer with the
|
|
122
|
+
// given mask layer subtracted out using the `xor` blend mode. When applied to the
|
|
123
|
+
// layer being masked, this creates an inverted mask where only areas _outside_
|
|
124
|
+
// of the mask layer are visible.
|
|
125
|
+
// https://developer.apple.com/documentation/coregraphics/cgblendmode/xor
|
|
126
|
+
let base = BaseAnimationLayer()
|
|
127
|
+
base.backgroundColor = .rgb(0, 0, 0)
|
|
128
|
+
base.addSublayer(maskLayer)
|
|
129
|
+
maskLayer.compositingFilter = "xor"
|
|
130
|
+
return base
|
|
131
|
+
|
|
132
|
+
case .none, .unknown:
|
|
133
|
+
return nil
|
|
134
|
+
}
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
extension Collection where Element == LayerModel {
|
|
140
|
+
/// Pairs each `LayerModel` within this array with
|
|
141
|
+
/// a `LayerModel` to use as its mask, if applicable
|
|
142
|
+
/// based on the layer's `MatteType` configuration.
|
|
143
|
+
/// - Assumes the layers are sorted in z-axis order.
|
|
144
|
+
fileprivate func pairedLayersAndMasks(context _: LayerContext) throws
|
|
145
|
+
-> [(layer: LayerModel, mask: (model: LayerModel, matteType: MatteType)?)]
|
|
146
|
+
{
|
|
147
|
+
var layersAndMasks = [(layer: LayerModel, mask: (model: LayerModel, matteType: MatteType)?)]()
|
|
148
|
+
var unprocessedLayers = reversed()
|
|
149
|
+
|
|
150
|
+
while let layer = unprocessedLayers.popLast() {
|
|
151
|
+
/// If a layer has a `MatteType`, then the next layer will be used as its `mask`
|
|
152
|
+
if
|
|
153
|
+
let matteType = layer.matte,
|
|
154
|
+
matteType != .none,
|
|
155
|
+
let maskLayer = unprocessedLayers.popLast()
|
|
156
|
+
{
|
|
157
|
+
layersAndMasks.append((layer: layer, mask: (model: maskLayer, matteType: matteType)))
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
else {
|
|
161
|
+
layersAndMasks.append((layer: layer, mask: nil))
|
|
162
|
+
}
|
|
163
|
+
}
|
|
164
|
+
|
|
165
|
+
return layersAndMasks
|
|
166
|
+
}
|
|
167
|
+
}
|
|
@@ -0,0 +1,94 @@
|
|
|
1
|
+
// Created by Cal Stephens on 1/10/22.
|
|
2
|
+
// Copyright © 2022 Airbnb Inc. All rights reserved.
|
|
3
|
+
|
|
4
|
+
import QuartzCore
|
|
5
|
+
|
|
6
|
+
// MARK: - GradientRenderLayer
|
|
7
|
+
|
|
8
|
+
/// A `CAGradientLayer` subclass used to render a gradient _outside_ the normal layer bounds
|
|
9
|
+
///
|
|
10
|
+
/// - `GradientFill.startPoint` and `GradientFill.endPoint` are expressed
|
|
11
|
+
/// with respect to the `bounds` of the `ShapeItemLayer`.
|
|
12
|
+
///
|
|
13
|
+
/// - The gradient itself is supposed to be rendered infinitely in all directions
|
|
14
|
+
/// (e.g. including outside of `bounds`). This is because `ShapeItemLayer` paths
|
|
15
|
+
/// don't necessarily sit within the layer's `bounds`.
|
|
16
|
+
///
|
|
17
|
+
/// - To support this, `GradientRenderLayer` tracks a `gradientReferenceBounds`
|
|
18
|
+
/// that `startPoint` / `endPoint` are calculated relative to.
|
|
19
|
+
/// The _actual_ `bounds` of this layer is padded by a large amount so that
|
|
20
|
+
/// the gradient can be drawn outside of the `gradientReferenceBounds`.
|
|
21
|
+
///
|
|
22
|
+
final class GradientRenderLayer: CAGradientLayer {
|
|
23
|
+
|
|
24
|
+
// MARK: Internal
|
|
25
|
+
|
|
26
|
+
/// The reference bounds within this layer that the gradient's
|
|
27
|
+
/// `startPoint` and `endPoint` should be calculated relative to
|
|
28
|
+
var gradientReferenceBounds: CGRect = .zero {
|
|
29
|
+
didSet {
|
|
30
|
+
if oldValue != gradientReferenceBounds {
|
|
31
|
+
updateLayout()
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
/// Converts the given `CGPoint` within `gradientReferenceBounds`
|
|
37
|
+
/// to a percentage value relative to the full `bounds` of this layer
|
|
38
|
+
/// - This converts absolute `startPoint` and `endPoint` values into
|
|
39
|
+
/// the percent-based values expected by Core Animation,
|
|
40
|
+
/// with respect to the custom bounds geometry used by this layer type.
|
|
41
|
+
func percentBasedPointInBounds(from referencePoint: CGPoint) -> CGPoint {
|
|
42
|
+
guard bounds.width > 0, bounds.height > 0 else {
|
|
43
|
+
LottieLogger.shared.assertionFailure("Size must be non-zero before an animation can be played")
|
|
44
|
+
return .zero
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
let pointInBounds = CGPoint(
|
|
48
|
+
x: referencePoint.x + gradientPadding,
|
|
49
|
+
y: referencePoint.y + gradientPadding)
|
|
50
|
+
|
|
51
|
+
return CGPoint(
|
|
52
|
+
x: CGFloat(pointInBounds.x) / bounds.width,
|
|
53
|
+
y: CGFloat(pointInBounds.y) / bounds.height)
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
// MARK: Private
|
|
57
|
+
|
|
58
|
+
/// Extra padding around the `gradientReferenceBounds` where the gradient is also rendered
|
|
59
|
+
/// - This specific value is arbitrary and can be increased if necessary.
|
|
60
|
+
/// Theoretically this should be "infinite", to match the behavior of
|
|
61
|
+
/// `CGContext.drawLinearGradient` with `[.drawsAfterEndLocation, .drawsBeforeStartLocation]`.
|
|
62
|
+
private let gradientPadding: CGFloat = 10_000
|
|
63
|
+
|
|
64
|
+
private func updateLayout() {
|
|
65
|
+
anchorPoint = .zero
|
|
66
|
+
|
|
67
|
+
bounds = CGRect(
|
|
68
|
+
x: gradientReferenceBounds.origin.x,
|
|
69
|
+
y: gradientReferenceBounds.origin.y,
|
|
70
|
+
width: gradientPadding + gradientReferenceBounds.width + gradientPadding,
|
|
71
|
+
height: gradientPadding + gradientReferenceBounds.height + gradientPadding)
|
|
72
|
+
|
|
73
|
+
// Align the center of this layer to be at the center point of its parent layer
|
|
74
|
+
let superlayerSize = superlayer?.frame.size ?? gradientReferenceBounds.size
|
|
75
|
+
|
|
76
|
+
transform = CATransform3DMakeTranslation(
|
|
77
|
+
(superlayerSize.width - bounds.width) / 2,
|
|
78
|
+
(superlayerSize.height - bounds.height) / 2,
|
|
79
|
+
0)
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
// MARK: CustomLayoutLayer
|
|
85
|
+
|
|
86
|
+
extension GradientRenderLayer: CustomLayoutLayer {
|
|
87
|
+
func layout(superlayerBounds: CGRect) {
|
|
88
|
+
gradientReferenceBounds = superlayerBounds
|
|
89
|
+
|
|
90
|
+
if let gradientMask = mask as? GradientRenderLayer {
|
|
91
|
+
gradientMask.layout(superlayerBounds: superlayerBounds)
|
|
92
|
+
}
|
|
93
|
+
}
|
|
94
|
+
}
|