lottie-ios 4.3.3 → 4.4.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.
- package/.github/workflows/main.yml +51 -58
- package/Lottie.xcodeproj/project.pbxproj +40 -0
- package/Lottie.xcodeproj/xcuserdata/calstephens.xcuserdatad/xcschemes/xcschememanagement.plist +4 -17
- package/Lottie.xcworkspace/xcshareddata/swiftpm/Package.resolved +37 -40
- package/Lottie.xcworkspace/xcuserdata/calstephens.xcuserdatad/UserInterfaceState.xcuserstate +0 -0
- package/Lottie.xcworkspace/xcuserdata/calstephens.xcuserdatad/xcdebugger/Breakpoints_v2.xcbkptlist +9 -666
- package/Package.resolved +20 -22
- package/Package.swift +4 -2
- package/README.md +21 -3
- package/Rakefile +52 -28
- package/Sources/PrivacyInfo.xcprivacy +23 -0
- package/Sources/Private/CoreAnimation/Animations/CAAnimation+TimingConfiguration.swift +1 -1
- package/Sources/Private/CoreAnimation/Animations/CALayer+addAnimation.swift +23 -13
- package/Sources/Private/CoreAnimation/Animations/CustomPathAnimation.swift +8 -2
- package/Sources/Private/CoreAnimation/Animations/EllipseAnimation.swift +7 -1
- package/Sources/Private/CoreAnimation/Animations/GradientAnimations.swift +15 -2
- package/Sources/Private/CoreAnimation/Animations/LayerProperty.swift +8 -1
- package/Sources/Private/CoreAnimation/Animations/RectangleAnimation.swift +8 -1
- package/Sources/Private/CoreAnimation/Animations/StarAnimation.swift +12 -1
- package/Sources/Private/CoreAnimation/Animations/StrokeAnimation.swift +0 -1
- package/Sources/Private/CoreAnimation/Animations/TransformAnimations.swift +19 -11
- package/Sources/Private/CoreAnimation/Animations/VisibilityAnimation.swift +45 -19
- package/Sources/Private/CoreAnimation/CoreAnimationLayer.swift +38 -3
- package/Sources/Private/CoreAnimation/Extensions/CALayer+fillBounds.swift +1 -1
- package/Sources/Private/CoreAnimation/Extensions/Keyframes+combined.swift +9 -2
- package/Sources/Private/CoreAnimation/Extensions/Keyframes+timeRemapping.swift +46 -0
- package/Sources/Private/CoreAnimation/Layers/AnimationLayer.swift +77 -13
- package/Sources/Private/CoreAnimation/Layers/BaseCompositionLayer.swift +1 -1
- package/Sources/Private/CoreAnimation/Layers/CALayer+setupLayerHierarchy.swift +2 -2
- package/Sources/Private/CoreAnimation/Layers/ImageLayer.swift +1 -1
- package/Sources/Private/CoreAnimation/Layers/LayerModel+makeAnimationLayer.swift +1 -7
- package/Sources/Private/CoreAnimation/Layers/PreCompLayer.swift +19 -53
- package/Sources/Private/CoreAnimation/Layers/ShapeItemLayer.swift +2 -2
- package/Sources/Private/CoreAnimation/Layers/ShapeLayer.swift +97 -31
- package/Sources/Private/CoreAnimation/Layers/SolidLayer.swift +5 -2
- package/Sources/Private/EmbeddedLibraries/EpoxyCore/Model/EpoxyModelArrayBuilder.swift +1 -1
- package/Sources/Private/EmbeddedLibraries/EpoxyCore/Model/EpoxyModelProperty.swift +10 -10
- package/Sources/Private/EmbeddedLibraries/EpoxyCore/SwiftUI/EpoxySwiftUIHostingController.swift +1 -1
- package/Sources/Private/EmbeddedLibraries/EpoxyCore/SwiftUI/EpoxySwiftUIHostingView.swift +1 -3
- package/Sources/Private/EmbeddedLibraries/EpoxyCore/SwiftUI/EpoxySwiftUIIntrinsicContentSizeInvalidator.swift +2 -0
- package/Sources/Private/EmbeddedLibraries/EpoxyCore/SwiftUI/EpoxySwiftUILayoutMargins.swift +2 -0
- package/Sources/Private/EmbeddedLibraries/EpoxyCore/SwiftUI/EpoxyableView+SwiftUIView.swift +2 -0
- package/Sources/Private/EmbeddedLibraries/EpoxyCore/SwiftUI/LayoutUtilities/MeasuringViewRepresentable.swift +2 -0
- package/Sources/Private/EmbeddedLibraries/EpoxyCore/SwiftUI/LayoutUtilities/SwiftUIMeasurementContainer.swift +2 -0
- package/Sources/Private/EmbeddedLibraries/EpoxyCore/SwiftUI/SwiftUIView.swift +2 -0
- package/Sources/Private/EmbeddedLibraries/EpoxyCore/SwiftUI/UIView+SwiftUIView.swift +2 -0
- package/Sources/Private/EmbeddedLibraries/EpoxyCore/SwiftUI/UIViewConfiguringSwiftUIView.swift +2 -0
- package/Sources/Private/EmbeddedLibraries/EpoxyCore/Views/ViewType.swift +2 -1
- package/Sources/Private/EmbeddedLibraries/LRUCache/LRUCache.swift +256 -0
- package/Sources/Private/EmbeddedLibraries/LRUCache/README.md +24 -0
- package/Sources/Private/EmbeddedLibraries/README.md +16 -0
- package/Sources/Private/EmbeddedLibraries/ZipFoundation/Archive+Helpers.swift +2 -2
- package/Sources/Private/EmbeddedLibraries/ZipFoundation/Archive+MemoryFile.swift +7 -7
- package/Sources/Private/EmbeddedLibraries/ZipFoundation/Data+Compression.swift +2 -2
- package/Sources/Private/EmbeddedLibraries/ZipFoundation/FileManager+ZIP.swift +5 -5
- package/Sources/Private/MainThread/LayerContainers/CompLayers/CompositionLayer.swift +1 -2
- package/Sources/Private/MainThread/LayerContainers/CompLayers/ImageCompositionLayer.swift +1 -3
- package/Sources/Private/MainThread/LayerContainers/CompLayers/MaskContainerLayer.swift +1 -2
- package/Sources/Private/MainThread/LayerContainers/CompLayers/PreCompositionLayer.swift +2 -3
- package/Sources/Private/MainThread/LayerContainers/CompLayers/SolidCompositionLayer.swift +2 -3
- package/Sources/Private/MainThread/LayerContainers/CompLayers/TextCompositionLayer.swift +5 -12
- package/Sources/Private/MainThread/LayerContainers/MainThreadAnimationLayer.swift +0 -1
- package/Sources/Private/MainThread/LayerContainers/Utility/CachedImageProvider.swift +11 -7
- package/Sources/Private/MainThread/LayerContainers/Utility/CompositionLayersInitializer.swift +3 -3
- package/Sources/Private/MainThread/LayerContainers/Utility/CoreTextRenderLayer.swift +12 -12
- package/Sources/Private/MainThread/LayerContainers/Utility/InvertedMatteLayer.swift +1 -3
- package/Sources/Private/MainThread/LayerContainers/Utility/LayerFontProvider.swift +0 -2
- package/Sources/Private/MainThread/LayerContainers/Utility/LayerImageProvider.swift +1 -3
- package/Sources/Private/MainThread/LayerContainers/Utility/LayerTextProvider.swift +0 -2
- package/Sources/Private/MainThread/LayerContainers/Utility/LayerTransformNode.swift +0 -2
- package/Sources/Private/MainThread/NodeRenderSystem/Extensions/ItemsExtension.swift +1 -3
- package/Sources/Private/MainThread/NodeRenderSystem/NodeProperties/Protocols/KeypathSearchable.swift +0 -1
- package/Sources/Private/MainThread/NodeRenderSystem/NodeProperties/Protocols/NodePropertyMap.swift +0 -1
- package/Sources/Private/MainThread/NodeRenderSystem/Nodes/OutputNodes/GroupOutputNode.swift +1 -3
- package/Sources/Private/MainThread/NodeRenderSystem/Nodes/OutputNodes/PassThroughOutputNode.swift +1 -2
- package/Sources/Private/MainThread/NodeRenderSystem/Nodes/OutputNodes/PathOutputNode.swift +0 -1
- package/Sources/Private/MainThread/NodeRenderSystem/Nodes/OutputNodes/Renderables/FillRenderer.swift +0 -2
- package/Sources/Private/MainThread/NodeRenderSystem/Nodes/OutputNodes/Renderables/GradientFillRenderer.swift +0 -1
- package/Sources/Private/MainThread/NodeRenderSystem/Nodes/OutputNodes/Renderables/GradientStrokeRenderer.swift +0 -1
- package/Sources/Private/MainThread/NodeRenderSystem/Nodes/OutputNodes/Renderables/LegacyGradientFillRenderer.swift +0 -1
- package/Sources/Private/MainThread/NodeRenderSystem/Nodes/OutputNodes/Renderables/StrokeRenderer.swift +2 -3
- package/Sources/Private/MainThread/NodeRenderSystem/Nodes/RenderContainers/GroupNode.swift +1 -3
- package/Sources/Private/MainThread/NodeRenderSystem/Nodes/RenderNodes/StrokeNode.swift +2 -2
- package/Sources/Private/MainThread/NodeRenderSystem/Nodes/Text/TextAnimatorNode.swift +0 -2
- package/Sources/Private/MainThread/NodeRenderSystem/Protocols/AnimatorNode.swift +0 -1
- package/Sources/Private/MainThread/NodeRenderSystem/Protocols/PathNode.swift +0 -2
- package/Sources/Private/MainThread/NodeRenderSystem/Protocols/RenderNode.swift +0 -2
- package/Sources/Private/MainThread/NodeRenderSystem/RenderLayers/ShapeContainerLayer.swift +0 -1
- package/Sources/Private/MainThread/NodeRenderSystem/RenderLayers/ShapeRenderLayer.swift +0 -1
- package/Sources/Private/Model/Assets/Asset.swift +2 -3
- package/Sources/Private/Model/Assets/AssetLibrary.swift +11 -11
- package/Sources/Private/Model/Assets/ImageAsset.swift +20 -0
- package/Sources/Private/Model/Assets/PrecompAsset.swift +0 -2
- package/Sources/Private/Model/DictionaryInitializable.swift +18 -10
- package/Sources/Private/Model/DotLottie/DotLottieImageProvider.swift +9 -6
- package/Sources/Private/Model/DotLottie/DotLottieUtils.swift +1 -1
- package/Sources/Private/Model/Extensions/KeyedDecodingContainerExtensions.swift +5 -4
- package/Sources/Private/Model/Keyframes/KeyframeData.swift +1 -4
- package/Sources/Private/Model/Keyframes/KeyframeGroup.swift +1 -3
- package/Sources/Private/Model/LayerEffects/DropShadowEffect.swift +0 -2
- package/Sources/Private/Model/LayerEffects/EffectValues/ColorEffectValue.swift +0 -2
- package/Sources/Private/Model/LayerEffects/EffectValues/EffectValue.swift +4 -5
- package/Sources/Private/Model/LayerEffects/EffectValues/Vector1DEffectValue.swift +0 -2
- package/Sources/Private/Model/LayerEffects/LayerEffect.swift +2 -3
- package/Sources/Private/Model/LayerStyles/DropShadowStyle.swift +0 -2
- package/Sources/Private/Model/LayerStyles/LayerStyle.swift +2 -3
- package/Sources/Private/Model/Layers/ImageLayerModel.swift +0 -2
- package/Sources/Private/Model/Layers/LayerModel.swift +16 -5
- package/Sources/Private/Model/Layers/PreCompLayerModel.swift +0 -2
- package/Sources/Private/Model/Layers/ShapeLayerModel.swift +0 -2
- package/Sources/Private/Model/Layers/SolidLayerModel.swift +0 -2
- package/Sources/Private/Model/Layers/TextLayerModel.swift +0 -2
- package/Sources/Private/Model/Objects/DashPattern.swift +1 -3
- package/Sources/Private/Model/Objects/Marker.swift +0 -2
- package/Sources/Private/Model/Objects/Mask.swift +0 -2
- package/Sources/Private/Model/Objects/Transform.swift +46 -9
- package/Sources/Private/Model/ShapeItems/Ellipse.swift +0 -2
- package/Sources/Private/Model/ShapeItems/Fill.swift +0 -2
- package/Sources/Private/Model/ShapeItems/GradientFill.swift +1 -3
- package/Sources/Private/Model/ShapeItems/GradientStroke.swift +1 -3
- package/Sources/Private/Model/ShapeItems/Group.swift +0 -2
- package/Sources/Private/Model/ShapeItems/Merge.swift +1 -3
- package/Sources/Private/Model/ShapeItems/Rectangle.swift +0 -2
- package/Sources/Private/Model/ShapeItems/Repeater.swift +0 -2
- package/Sources/Private/Model/ShapeItems/RoundedCorners.swift +0 -2
- package/Sources/Private/Model/ShapeItems/Shape.swift +0 -2
- package/Sources/Private/Model/ShapeItems/ShapeItem.swift +2 -3
- package/Sources/Private/Model/ShapeItems/ShapeTransform.swift +0 -2
- package/Sources/Private/Model/ShapeItems/Star.swift +1 -3
- package/Sources/Private/Model/ShapeItems/Stroke.swift +0 -2
- package/Sources/Private/Model/ShapeItems/Trim.swift +1 -3
- package/Sources/Private/Model/Text/Font.swift +0 -2
- package/Sources/Private/Model/Text/Glyph.swift +0 -2
- package/Sources/Private/Model/Text/TextAnimator.swift +0 -2
- package/Sources/Private/Model/Text/TextDocument.swift +2 -4
- package/Sources/Private/Utility/Debugging/AnimatorNodeDebugging.swift +0 -2
- package/Sources/Private/Utility/Debugging/LayerDebugging.swift +4 -5
- package/Sources/Private/Utility/Extensions/AnimationKeypathExtension.swift +2 -3
- package/Sources/Private/Utility/Extensions/DataExtension.swift +0 -1
- package/Sources/Private/Utility/Extensions/StringExtensions.swift +5 -0
- package/Sources/Private/Utility/Helpers/AnimationContext.swift +2 -3
- package/Sources/Private/Utility/Helpers/AnyEquatable.swift +0 -2
- package/Sources/Private/Utility/Helpers/Binding+Map.swift +2 -0
- package/Sources/Private/Utility/Helpers/View+ValueChanged.swift +2 -0
- package/Sources/Private/Utility/LottieAnimationSource.swift +11 -1
- package/Sources/Private/Utility/Primitives/BezierPath.swift +2 -3
- package/Sources/Private/Utility/Primitives/CGPointExtension.swift +1 -1
- package/Sources/Private/Utility/Primitives/ColorExtension.swift +1 -2
- package/Sources/Private/Utility/Primitives/VectorsExtensions.swift +6 -6
- package/Sources/Public/Animation/LottieAnimation.swift +2 -2
- package/Sources/Public/Animation/LottieAnimationHelpers.swift +7 -5
- package/Sources/Public/Animation/LottieAnimationLayer.swift +30 -43
- package/Sources/Public/Animation/LottieAnimationView.swift +18 -8
- package/Sources/Public/Animation/LottieAnimationViewInitializers.swift +3 -3
- package/Sources/Public/Animation/LottiePlaybackMode.swift +53 -0
- package/Sources/Public/Animation/LottieView.swift +145 -48
- package/Sources/Public/AnimationCache/AnimationCacheProvider.swift +0 -2
- package/Sources/Public/AnimationCache/DefaultAnimationCache.swift +21 -7
- package/Sources/Public/AnimationCache/LRUAnimationCache.swift +0 -2
- package/Sources/Public/Controls/AnimatedButton.swift +4 -6
- package/Sources/Public/Controls/AnimatedControl.swift +1 -3
- package/Sources/Public/Controls/AnimatedSwitch.swift +1 -3
- package/Sources/Public/Controls/LottieButton.swift +2 -1
- package/Sources/Public/Controls/LottieSwitch.swift +2 -0
- package/Sources/Public/DotLottie/Cache/DotLottieCache.swift +17 -4
- package/Sources/Public/DotLottie/Cache/DotLottieCacheProvider.swift +1 -3
- package/Sources/Public/DotLottie/DotLottieConfiguration.swift +53 -5
- package/Sources/Public/DotLottie/DotLottieFile.swift +4 -3
- package/Sources/Public/DotLottie/DotLottieFileHelpers.swift +18 -11
- package/Sources/Public/DynamicProperties/AnimationKeypath.swift +0 -2
- package/Sources/Public/DynamicProperties/AnyValueProvider.swift +0 -1
- package/Sources/Public/DynamicProperties/ValueProviders/ColorValueProvider.swift +2 -2
- package/Sources/Public/DynamicProperties/ValueProviders/FloatValueProvider.swift +1 -1
- package/Sources/Public/DynamicProperties/ValueProviders/GradientValueProvider.swift +1 -1
- package/Sources/Public/DynamicProperties/ValueProviders/PointValueProvider.swift +1 -1
- package/Sources/Public/DynamicProperties/ValueProviders/SizeValueProvider.swift +1 -1
- package/Sources/Public/FontProvider/AnimationFontProvider.swift +0 -2
- package/Sources/Public/ImageProvider/AnimationImageProvider.swift +0 -2
- package/Sources/Public/Keyframes/Interpolatable.swift +26 -0
- package/Sources/Public/Primitives/LottieColor.swift +0 -2
- package/Sources/Public/Primitives/Vectors.swift +0 -2
- package/Sources/Public/TextProvider/AnimationTextProvider.swift +0 -2
- package/Sources/Public/iOS/AnimationSubview.swift +0 -1
- package/Sources/Public/iOS/BundleImageProvider.swift +3 -8
- package/Sources/Public/iOS/Compatibility/CompatibleAnimationView.swift +4 -8
- package/Sources/Public/iOS/LottieAnimationViewBase.swift +5 -2
- package/Sources/Public/iOS/UIColorExtension.swift +1 -2
- package/Sources/Public/macOS/BundleImageProvider.macOS.swift +3 -6
- package/lottie-ios.podspec +6 -2
- package/package.json +1 -1
- package/Lottie.xcodeproj/project.xcworkspace/xcuserdata/cal.xcuserdatad/UserInterfaceState.xcuserstate +0 -0
- package/Lottie.xcodeproj/project.xcworkspace/xcuserdata/calstephens.xcuserdatad/UserInterfaceState.xcuserstate +0 -0
- package/Lottie.xcodeproj/xcuserdata/cal.xcuserdatad/xcschemes/xcschememanagement.plist +0 -37
- package/Lottie.xcworkspace/xcuserdata/cal.xcuserdatad/UserInterfaceState.xcuserstate +0 -0
- package/Lottie.xcworkspace/xcuserdata/cal.xcuserdatad/xcdebugger/Breakpoints_v2.xcbkptlist +0 -6
- package/Lottie.xcworkspace/xcuserdata/cal.xcuserdatad/xcdebugger/Expressions.xcexplist +0 -153
- package/Lottie.xcworkspace/xcuserdata/calstephens.xcuserdatad/IDEFindNavigatorScopes.plist +0 -5
- package/Lottie.xcworkspace/xcuserdata/calstephens.xcuserdatad/xcdebugger/Expressions.xcexplist +0 -231
- package/Lottie.xcworkspace/xcuserdata/calstephens.xcuserdatad/xcschemes/xcschememanagement.plist +0 -37
|
@@ -10,28 +10,54 @@ extension CALayer {
|
|
|
10
10
|
inFrame: AnimationFrameTime,
|
|
11
11
|
outFrame: AnimationFrameTime,
|
|
12
12
|
context: LayerAnimationContext)
|
|
13
|
+
throws
|
|
13
14
|
{
|
|
14
|
-
|
|
15
|
-
|
|
15
|
+
/// If this layer uses `complexTimeRemapping`, use the `addAnimation` codepath
|
|
16
|
+
/// which uses `Keyframes.manuallyInterpolatedWithTimeRemapping`.
|
|
17
|
+
if context.mustUseComplexTimeRemapping {
|
|
18
|
+
let isHiddenKeyframes = KeyframeGroup(keyframes: [
|
|
19
|
+
Keyframe(value: true, time: 0, isHold: true), // hidden, before `inFrame`
|
|
20
|
+
Keyframe(value: false, time: inFrame, isHold: true), // visible
|
|
21
|
+
Keyframe(value: true, time: outFrame, isHold: true), // hidden, after `outFrame`
|
|
22
|
+
])
|
|
16
23
|
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
24
|
+
try addAnimation(
|
|
25
|
+
for: .isHidden,
|
|
26
|
+
keyframes: isHiddenKeyframes.map { Hold(value: $0) },
|
|
27
|
+
value: { $0.value },
|
|
28
|
+
context: context)
|
|
29
|
+
}
|
|
22
30
|
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
NSNumber(value: 0.0),
|
|
30
|
-
NSNumber(value: max(Double(context.progressTime(for: inFrame)), 0)),
|
|
31
|
-
NSNumber(value: min(Double(context.progressTime(for: outFrame)), 1)),
|
|
32
|
-
NSNumber(value: 1.0),
|
|
33
|
-
]
|
|
31
|
+
/// Otherwise continue using the legacy codepath that doesn't support complex time remapping.
|
|
32
|
+
/// - TODO: We could remove this codepath in favor of always using the simpler codepath above,
|
|
33
|
+
/// but would have to solve https://github.com/airbnb/lottie-ios/pull/2254 for that codepath.
|
|
34
|
+
else {
|
|
35
|
+
let animation = CAKeyframeAnimation(keyPath: #keyPath(isHidden))
|
|
36
|
+
animation.calculationMode = .discrete
|
|
34
37
|
|
|
35
|
-
|
|
38
|
+
animation.values = [
|
|
39
|
+
true, // hidden, before `inFrame`
|
|
40
|
+
false, // visible
|
|
41
|
+
true, // hidden, after `outFrame`
|
|
42
|
+
]
|
|
43
|
+
|
|
44
|
+
// From the documentation of `keyTimes`:
|
|
45
|
+
// - If the calculationMode is set to discrete, the first value in the array
|
|
46
|
+
// must be 0.0 and the last value must be 1.0. The array should have one more
|
|
47
|
+
// entry than appears in the values array. For example, if there are two values,
|
|
48
|
+
// there should be three key times.
|
|
49
|
+
animation.keyTimes = [
|
|
50
|
+
NSNumber(value: 0.0),
|
|
51
|
+
NSNumber(value: max(Double(try context.progressTime(for: inFrame)), 0)),
|
|
52
|
+
// Anything visible during the last frame should stay visible until the absolute end of the animation.
|
|
53
|
+
// - This matches the behavior of the main thread rendering engine.
|
|
54
|
+
context.simpleTimeRemapping(outFrame) == context.animation.endFrame
|
|
55
|
+
? NSNumber(value: Double(1.0))
|
|
56
|
+
: NSNumber(value: min(Double(try context.progressTime(for: outFrame)), 1)),
|
|
57
|
+
NSNumber(value: 1.0),
|
|
58
|
+
]
|
|
59
|
+
|
|
60
|
+
add(animation, timedWith: context)
|
|
61
|
+
}
|
|
36
62
|
}
|
|
37
63
|
}
|
|
@@ -1,7 +1,6 @@
|
|
|
1
1
|
// Created by Cal Stephens on 12/13/21.
|
|
2
2
|
// Copyright © 2021 Airbnb Inc. All rights reserved.
|
|
3
3
|
|
|
4
|
-
import Foundation
|
|
5
4
|
import QuartzCore
|
|
6
5
|
|
|
7
6
|
// MARK: - CoreAnimationLayer
|
|
@@ -161,7 +160,7 @@ final class CoreAnimationLayer: BaseAnimationLayer {
|
|
|
161
160
|
// allocate a very large amount of memory (400mb+).
|
|
162
161
|
// - Alternatively this layer could subclass `CATransformLayer`,
|
|
163
162
|
// but this causes Core Animation to emit unnecessary logs.
|
|
164
|
-
if var pendingAnimationConfiguration
|
|
163
|
+
if var pendingAnimationConfiguration {
|
|
165
164
|
pendingAnimationConfigurationModification?(&pendingAnimationConfiguration.animationConfiguration)
|
|
166
165
|
pendingAnimationConfigurationModification = nil
|
|
167
166
|
self.pendingAnimationConfiguration = nil
|
|
@@ -249,6 +248,8 @@ final class CoreAnimationLayer: BaseAnimationLayer {
|
|
|
249
248
|
try setupLayerHierarchy(
|
|
250
249
|
for: animation.layers,
|
|
251
250
|
context: layerContext)
|
|
251
|
+
|
|
252
|
+
try validateReasonableNumberOfTimeRemappingLayers()
|
|
252
253
|
}
|
|
253
254
|
|
|
254
255
|
/// Immediately builds and begins playing `CAAnimation`s for each sublayer
|
|
@@ -387,7 +388,10 @@ extension CoreAnimationLayer: RootAnimationLayer {
|
|
|
387
388
|
let requiredAnimationConfiguration = AnimationConfiguration(
|
|
388
389
|
animationContext: AnimationContext(
|
|
389
390
|
playFrom: animation.startFrame,
|
|
390
|
-
|
|
391
|
+
// Normal animation playback (like when looping) skips the last frame.
|
|
392
|
+
// However when the animation is paused, we need to be able to render the final frame.
|
|
393
|
+
// To allow this we have to extend the length of the animation by one frame.
|
|
394
|
+
playTo: animation.endFrame + 1,
|
|
391
395
|
closure: nil),
|
|
392
396
|
timingConfiguration: CAMediaTimingConfiguration(speed: 0))
|
|
393
397
|
|
|
@@ -529,6 +533,18 @@ extension CoreAnimationLayer: RootAnimationLayer {
|
|
|
529
533
|
}
|
|
530
534
|
}
|
|
531
535
|
|
|
536
|
+
/// Time remapping in the Core Animation rendering engine requires manually interpolating
|
|
537
|
+
/// every frame of every animation. For very large animations with a huge number of layers,
|
|
538
|
+
/// this can be prohibitively expensive.
|
|
539
|
+
func validateReasonableNumberOfTimeRemappingLayers() throws {
|
|
540
|
+
try layerContext.compatibilityAssert(
|
|
541
|
+
numberOfLayersWithTimeRemapping < 500,
|
|
542
|
+
"""
|
|
543
|
+
This animation has a very large number of layers with time remapping (\(numberOfLayersWithTimeRemapping)),
|
|
544
|
+
so will perform poorly with the Core Animation rendering engine.
|
|
545
|
+
""")
|
|
546
|
+
}
|
|
547
|
+
|
|
532
548
|
}
|
|
533
549
|
|
|
534
550
|
// MARK: - CALayer + allSublayers
|
|
@@ -546,4 +562,23 @@ extension CALayer {
|
|
|
546
562
|
|
|
547
563
|
return allSublayers
|
|
548
564
|
}
|
|
565
|
+
|
|
566
|
+
/// The number of layers in this layer hierarchy that have a time remapping applied
|
|
567
|
+
@nonobjc
|
|
568
|
+
var numberOfLayersWithTimeRemapping: Int {
|
|
569
|
+
var numberOfSublayersWithTimeRemapping = 0
|
|
570
|
+
|
|
571
|
+
for sublayer in sublayers ?? [] {
|
|
572
|
+
if
|
|
573
|
+
let preCompLayer = sublayer as? PreCompLayer,
|
|
574
|
+
preCompLayer.preCompLayer.timeRemapping != nil
|
|
575
|
+
{
|
|
576
|
+
numberOfSublayersWithTimeRemapping += preCompLayer.allSublayers.count
|
|
577
|
+
} else {
|
|
578
|
+
numberOfSublayersWithTimeRemapping += sublayer.numberOfLayersWithTimeRemapping
|
|
579
|
+
}
|
|
580
|
+
}
|
|
581
|
+
|
|
582
|
+
return numberOfSublayersWithTimeRemapping
|
|
583
|
+
}
|
|
549
584
|
}
|
|
@@ -10,7 +10,7 @@ extension CALayer {
|
|
|
10
10
|
/// without setting `frame` (which is not permitted if the layer can rotate)
|
|
11
11
|
@nonobjc
|
|
12
12
|
func fillBoundsOfSuperlayer() {
|
|
13
|
-
guard let superlayer
|
|
13
|
+
guard let superlayer else { return }
|
|
14
14
|
|
|
15
15
|
if let customLayerLayer = self as? CustomLayoutLayer {
|
|
16
16
|
customLayerLayer.layout(superlayerBounds: superlayer.bounds)
|
|
@@ -256,7 +256,11 @@ enum Keyframes {
|
|
|
256
256
|
|
|
257
257
|
let minimumTime = times.min() ?? 0
|
|
258
258
|
let maximumTime = times.max() ?? 0
|
|
259
|
-
|
|
259
|
+
|
|
260
|
+
// We disable Core Animation interpolation when using manually interpolated keyframes,
|
|
261
|
+
// so we don't animate between these values. To prevent the animation from being choppy
|
|
262
|
+
// even at low playback speed, we have to interpolate at a very high fidelity.
|
|
263
|
+
let animationLocalTimeRange = stride(from: minimumTime, to: maximumTime, by: 0.1)
|
|
260
264
|
|
|
261
265
|
let interpolatedKeyframes = try animationLocalTimeRange.compactMap { localTime -> Keyframe<CombinedResult>? in
|
|
262
266
|
let interpolatedValues = untypedInterpolators.map { interpolator in
|
|
@@ -272,7 +276,10 @@ enum Keyframes {
|
|
|
272
276
|
|
|
273
277
|
return Keyframe(
|
|
274
278
|
value: combinedResult,
|
|
275
|
-
time: AnimationFrameTime(localTime)
|
|
279
|
+
time: AnimationFrameTime(localTime),
|
|
280
|
+
// Since we already manually interpolated the keyframes, have Core Animation display
|
|
281
|
+
// each value as a static keyframe rather than trying to interpolate between them.
|
|
282
|
+
isHold: true)
|
|
276
283
|
}
|
|
277
284
|
|
|
278
285
|
return KeyframeGroup(keyframes: ContiguousArray(interpolatedKeyframes))
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
// Created by Cal Stephens on 1/8/24.
|
|
2
|
+
// Copyright © 2024 Airbnb Inc. All rights reserved.
|
|
3
|
+
|
|
4
|
+
extension Keyframes {
|
|
5
|
+
/// Manually interpolates the given keyframes, and applies `context.complexTimeRemapping`.
|
|
6
|
+
/// - Since `complexTimeRemapping` is a mapping from "global time" to "local time",
|
|
7
|
+
/// we have to manually interpolate the keyframes at every frame in the animation.
|
|
8
|
+
static func manuallyInterpolatedWithTimeRemapping<T: AnyInterpolatable>(
|
|
9
|
+
_ keyframes: KeyframeGroup<T>,
|
|
10
|
+
context: LayerAnimationContext)
|
|
11
|
+
-> KeyframeGroup<T>
|
|
12
|
+
{
|
|
13
|
+
let minimumTime = context.animation.startFrame
|
|
14
|
+
let maximumTime = context.animation.endFrame
|
|
15
|
+
let animationLocalTimeRange = stride(from: minimumTime, to: maximumTime, by: 1.0)
|
|
16
|
+
|
|
17
|
+
let interpolator = keyframes.interpolator
|
|
18
|
+
|
|
19
|
+
// Since potentially many global times can refer to the same local time,
|
|
20
|
+
// we can cache and reused these local-time values.
|
|
21
|
+
var localTimeCache = [AnimationFrameTime: T]()
|
|
22
|
+
|
|
23
|
+
let interpolatedRemappedKeyframes = animationLocalTimeRange.compactMap { globalTime -> Keyframe<T>? in
|
|
24
|
+
let remappedLocalTime = context.complexTimeRemapping(globalTime)
|
|
25
|
+
|
|
26
|
+
let valueAtRemappedTime: T
|
|
27
|
+
if let cachedValue = localTimeCache[remappedLocalTime] {
|
|
28
|
+
valueAtRemappedTime = cachedValue
|
|
29
|
+
} else if let interpolatedValue = interpolator.value(frame: remappedLocalTime) as? T {
|
|
30
|
+
valueAtRemappedTime = interpolatedValue
|
|
31
|
+
localTimeCache[remappedLocalTime] = interpolatedValue
|
|
32
|
+
} else {
|
|
33
|
+
LottieLogger.shared.assertionFailure("""
|
|
34
|
+
Failed to cast untyped keyframe values to expected type. This is an internal error.
|
|
35
|
+
""")
|
|
36
|
+
return nil
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
return Keyframe(
|
|
40
|
+
value: valueAtRemappedTime,
|
|
41
|
+
time: AnimationFrameTime(globalTime))
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
return KeyframeGroup(keyframes: ContiguousArray(interpolatedRemappedKeyframes))
|
|
45
|
+
}
|
|
46
|
+
}
|
|
@@ -52,8 +52,37 @@ struct LayerAnimationContext {
|
|
|
52
52
|
var recordHierarchyKeypath: ((String) -> Void)?
|
|
53
53
|
|
|
54
54
|
/// A closure that remaps the given frame in the child layer's local time to a frame
|
|
55
|
-
/// in the animation's overall global time
|
|
56
|
-
|
|
55
|
+
/// in the animation's overall global time.
|
|
56
|
+
/// - This time remapping is simple and only used `preCompLayer.timeStretch` and `preCompLayer.startTime`,
|
|
57
|
+
/// so is a trivial function and is invertible. This allows us to invert the time remapping from
|
|
58
|
+
/// "global time to local time" to instead be "local time to global time".
|
|
59
|
+
private(set) var simpleTimeRemapping: ((_ localTime: AnimationFrameTime) -> AnimationFrameTime) = { $0 }
|
|
60
|
+
|
|
61
|
+
/// A complex time remapping closure that remaps the given frame in the animation's overall global time
|
|
62
|
+
/// into the child layer's local time.
|
|
63
|
+
/// - This time remapping is arbitrarily complex because it includes the full `preCompLayer.timeRemapping`.
|
|
64
|
+
/// - Since it isn't possible to invert the time remapping function, this can only be applied by converting
|
|
65
|
+
/// from global time to local time. This requires using `Keyframes.manuallyInterpolatedWithTimeRemapping`.
|
|
66
|
+
private(set) var complexTimeRemapping: ((_ globalTime: AnimationFrameTime) -> AnimationFrameTime) = { $0 }
|
|
67
|
+
|
|
68
|
+
/// Whether or not this layer is required to use the `complexTimeRemapping` via
|
|
69
|
+
/// the more expensive `Keyframes.manuallyInterpolatedWithTimeRemapping` codepath.
|
|
70
|
+
var mustUseComplexTimeRemapping = false
|
|
71
|
+
|
|
72
|
+
/// The duration of the animation
|
|
73
|
+
var animationDuration: AnimationFrameTime {
|
|
74
|
+
// Normal animation playback (like when looping) skips the last frame.
|
|
75
|
+
// However when the animation is paused, we need to be able to render the final frame.
|
|
76
|
+
// To allow this we have to extend the length of the animation by one frame.
|
|
77
|
+
let animationEndFrame: AnimationFrameTime
|
|
78
|
+
if timingConfiguration.speed == 0 {
|
|
79
|
+
animationEndFrame = animation.endFrame + 1
|
|
80
|
+
} else {
|
|
81
|
+
animationEndFrame = animation.endFrame
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
return Double(animationEndFrame - animation.startFrame) / animation.framerate
|
|
85
|
+
}
|
|
57
86
|
|
|
58
87
|
/// Adds the given component string to the `AnimationKeypath` stored
|
|
59
88
|
/// that describes the current path being configured by this context value
|
|
@@ -64,28 +93,63 @@ struct LayerAnimationContext {
|
|
|
64
93
|
}
|
|
65
94
|
|
|
66
95
|
/// The `AnimationProgressTime` for the given `AnimationFrameTime` within this layer,
|
|
67
|
-
/// accounting for the `
|
|
68
|
-
func progressTime(for frame: AnimationFrameTime) -> AnimationProgressTime {
|
|
69
|
-
|
|
96
|
+
/// accounting for the `simpleTimeRemapping` applied to this layer.
|
|
97
|
+
func progressTime(for frame: AnimationFrameTime) throws -> AnimationProgressTime {
|
|
98
|
+
try compatibilityAssert(
|
|
99
|
+
!mustUseComplexTimeRemapping,
|
|
100
|
+
"LayerAnimationContext.time(forFrame:) does not support complex time remapping")
|
|
101
|
+
|
|
102
|
+
let animationFrameCount = animationDuration * animation.framerate
|
|
103
|
+
return (simpleTimeRemapping(frame) - animation.startFrame) / animationFrameCount
|
|
70
104
|
}
|
|
71
105
|
|
|
72
106
|
/// The real-time `TimeInterval` for the given `AnimationFrameTime` within this layer,
|
|
73
|
-
/// accounting for the `
|
|
74
|
-
func time(
|
|
75
|
-
|
|
107
|
+
/// accounting for the `simpleTimeRemapping` applied to this layer.
|
|
108
|
+
func time(forFrame frame: AnimationFrameTime) throws -> TimeInterval {
|
|
109
|
+
try compatibilityAssert(
|
|
110
|
+
!mustUseComplexTimeRemapping,
|
|
111
|
+
"LayerAnimationContext.time(forFrame:) does not support complex time remapping")
|
|
112
|
+
|
|
113
|
+
return animation.time(forFrame: simpleTimeRemapping(frame))
|
|
76
114
|
}
|
|
77
115
|
|
|
78
|
-
/// Chains an additional
|
|
79
|
-
func
|
|
80
|
-
_
|
|
116
|
+
/// Chains an additional time remapping closure onto the `simpleTimeRemapping` closure
|
|
117
|
+
func withSimpleTimeRemapping(
|
|
118
|
+
_ additionalSimpleTimeRemapping: @escaping (_ localTime: AnimationFrameTime) -> AnimationFrameTime)
|
|
81
119
|
-> LayerAnimationContext
|
|
82
120
|
{
|
|
83
121
|
var copy = self
|
|
84
|
-
copy.
|
|
85
|
-
|
|
122
|
+
copy.simpleTimeRemapping = { [existingSimpleTimeRemapping = simpleTimeRemapping] time in
|
|
123
|
+
existingSimpleTimeRemapping(additionalSimpleTimeRemapping(time))
|
|
86
124
|
}
|
|
87
125
|
return copy
|
|
88
126
|
}
|
|
127
|
+
|
|
128
|
+
/// Chains an additional time remapping closure onto the `complexTimeRemapping` closure.
|
|
129
|
+
/// - If `required` is `true`, all subsequent child layers will be required to use the expensive
|
|
130
|
+
/// `complexTimeRemapping` / `Keyframes.manuallyInterpolatedWithTimeRemapping` codepath.
|
|
131
|
+
/// - `required: true` is necessary when this time remapping is not available via `simpleTimeRemapping`.
|
|
132
|
+
func withComplexTimeRemapping(
|
|
133
|
+
required: Bool,
|
|
134
|
+
_ additionalComplexTimeRemapping: @escaping (_ globalTime: AnimationFrameTime) -> AnimationFrameTime)
|
|
135
|
+
-> LayerAnimationContext
|
|
136
|
+
{
|
|
137
|
+
var copy = self
|
|
138
|
+
copy.mustUseComplexTimeRemapping = copy.mustUseComplexTimeRemapping || required
|
|
139
|
+
copy.complexTimeRemapping = { [existingComplexTimeRemapping = complexTimeRemapping] time in
|
|
140
|
+
additionalComplexTimeRemapping(existingComplexTimeRemapping(time))
|
|
141
|
+
}
|
|
142
|
+
return copy
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
/// Returns a copy of this context with time remapping removed
|
|
146
|
+
func withoutTimeRemapping() -> LayerAnimationContext {
|
|
147
|
+
var copy = self
|
|
148
|
+
copy.simpleTimeRemapping = { $0 }
|
|
149
|
+
copy.complexTimeRemapping = { $0 }
|
|
150
|
+
copy.mustUseComplexTimeRemapping = false
|
|
151
|
+
return copy
|
|
152
|
+
}
|
|
89
153
|
}
|
|
90
154
|
|
|
91
155
|
// MARK: - LoggingState
|
|
@@ -63,7 +63,7 @@ class BaseCompositionLayer: BaseAnimationLayer {
|
|
|
63
63
|
if renderLayerContents {
|
|
64
64
|
try contentsLayer.addOpacityAnimation(for: baseLayerModel.transform, context: transformContext)
|
|
65
65
|
|
|
66
|
-
contentsLayer.addVisibilityAnimation(
|
|
66
|
+
try contentsLayer.addVisibilityAnimation(
|
|
67
67
|
inFrame: CGFloat(baseLayerModel.inFrame),
|
|
68
68
|
outFrame: CGFloat(baseLayerModel.outFrame),
|
|
69
69
|
context: context)
|
|
@@ -67,7 +67,7 @@ extension CALayer {
|
|
|
67
67
|
|
|
68
68
|
// Create the `mask` layer for this layer, if it has a `MatteType`
|
|
69
69
|
if
|
|
70
|
-
let mask
|
|
70
|
+
let mask,
|
|
71
71
|
let maskLayer = try maskLayer(for: mask.model, type: mask.matteType, context: context)
|
|
72
72
|
{
|
|
73
73
|
let maskParentTransformLayer = makeParentTransformLayer(
|
|
@@ -138,7 +138,7 @@ extension CALayer {
|
|
|
138
138
|
|
|
139
139
|
}
|
|
140
140
|
|
|
141
|
-
extension Collection
|
|
141
|
+
extension Collection<LayerModel> {
|
|
142
142
|
/// Pairs each `LayerModel` within this array with
|
|
143
143
|
/// a `LayerModel` to use as its mask, if applicable
|
|
144
144
|
/// based on the layer's `MatteType` configuration.
|
|
@@ -1,8 +1,6 @@
|
|
|
1
1
|
// Created by Cal Stephens on 12/20/21.
|
|
2
2
|
// Copyright © 2021 Airbnb Inc. All rights reserved.
|
|
3
3
|
|
|
4
|
-
import QuartzCore
|
|
5
|
-
|
|
6
4
|
// MARK: - LayerContext
|
|
7
5
|
|
|
8
6
|
/// Context available when constructing an `AnimationLayer`
|
|
@@ -53,11 +51,7 @@ extension LayerModel {
|
|
|
53
51
|
case (.null, _):
|
|
54
52
|
return TransformLayer(layerModel: self)
|
|
55
53
|
|
|
56
|
-
|
|
57
|
-
try context.logCompatibilityIssue("""
|
|
58
|
-
Unexpected layer type combination ("\(type)" and "\(Swift.type(of: self))")
|
|
59
|
-
""")
|
|
60
|
-
|
|
54
|
+
case (.unknown, _), (.precomp, _), (.solid, _), (.image, _), (.shape, _), (.text, _):
|
|
61
55
|
return nil
|
|
62
56
|
}
|
|
63
57
|
}
|
|
@@ -27,12 +27,13 @@ final class PreCompLayer: BaseCompositionLayer {
|
|
|
27
27
|
}
|
|
28
28
|
|
|
29
29
|
preCompLayer = typedLayer.preCompLayer
|
|
30
|
-
timeRemappingInterpolator = typedLayer.timeRemappingInterpolator
|
|
31
30
|
super.init(layer: typedLayer)
|
|
32
31
|
}
|
|
33
32
|
|
|
34
33
|
// MARK: Internal
|
|
35
34
|
|
|
35
|
+
let preCompLayer: PreCompLayerModel
|
|
36
|
+
|
|
36
37
|
/// Post-init setup for `PreCompLayer`s.
|
|
37
38
|
/// Should always be called after `PreCompLayer.init(preCompLayer:)`.
|
|
38
39
|
///
|
|
@@ -51,12 +52,6 @@ final class PreCompLayer: BaseCompositionLayer {
|
|
|
51
52
|
/// - `PreCompLayer.init(preCompLayer:context:)`
|
|
52
53
|
///
|
|
53
54
|
func setup(context: LayerContext) throws {
|
|
54
|
-
if let timeRemappingKeyframes = preCompLayer.timeRemapping {
|
|
55
|
-
timeRemappingInterpolator = try .timeRemapping(keyframes: timeRemappingKeyframes, context: context)
|
|
56
|
-
} else {
|
|
57
|
-
timeRemappingInterpolator = nil
|
|
58
|
-
}
|
|
59
|
-
|
|
60
55
|
try setupLayerHierarchy(
|
|
61
56
|
for: context.animation.assetLibrary?.precompAssets[preCompLayer.referenceID]?.layers ?? [],
|
|
62
57
|
context: context)
|
|
@@ -67,24 +62,28 @@ final class PreCompLayer: BaseCompositionLayer {
|
|
|
67
62
|
context = context.addingKeypathComponent(preCompLayer.name)
|
|
68
63
|
try setupLayerAnimations(context: context)
|
|
69
64
|
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
let contextForChildren = context
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
65
|
+
let timeRemappingInterpolator = preCompLayer.timeRemapping.flatMap { KeyframeInterpolator(keyframes: $0.keyframes) }
|
|
66
|
+
|
|
67
|
+
let contextForChildren = context
|
|
68
|
+
// `timeStretch` and `startTime` are a simple linear function so can be inverted from a
|
|
69
|
+
// "global time to local time" function into the simpler "local time to global time".
|
|
70
|
+
.withSimpleTimeRemapping { [preCompLayer] layerLocalFrame in
|
|
71
|
+
(layerLocalFrame * AnimationFrameTime(preCompLayer.timeStretch)) + AnimationFrameTime(preCompLayer.startTime)
|
|
72
|
+
}
|
|
73
|
+
// `timeRemappingInterpolator` is arbitrarily complex and cannot be inverted,
|
|
74
|
+
// so can only be applied via `complexTimeRemapping` from global time to local time.
|
|
75
|
+
.withComplexTimeRemapping(required: preCompLayer.timeRemapping != nil) { [preCompLayer] globalTime in
|
|
76
|
+
if let timeRemappingInterpolator {
|
|
77
|
+
let remappedLocalTime = timeRemappingInterpolator.value(frame: globalTime) as! LottieVector1D
|
|
78
|
+
return remappedLocalTime.cgFloatValue * context.animation.framerate
|
|
79
|
+
} else {
|
|
80
|
+
return (globalTime - preCompLayer.startTime) / preCompLayer.timeStretch
|
|
81
|
+
}
|
|
77
82
|
}
|
|
78
|
-
}
|
|
79
83
|
|
|
80
84
|
try setupChildAnimations(context: contextForChildren)
|
|
81
85
|
}
|
|
82
86
|
|
|
83
|
-
// MARK: Private
|
|
84
|
-
|
|
85
|
-
private let preCompLayer: PreCompLayerModel
|
|
86
|
-
private var timeRemappingInterpolator: KeyframeInterpolator<AnimationFrameTime>?
|
|
87
|
-
|
|
88
87
|
}
|
|
89
88
|
|
|
90
89
|
// MARK: CustomLayoutLayer
|
|
@@ -104,36 +103,3 @@ extension PreCompLayer: CustomLayoutLayer {
|
|
|
104
103
|
contentsLayer.masksToBounds = true
|
|
105
104
|
}
|
|
106
105
|
}
|
|
107
|
-
|
|
108
|
-
extension KeyframeInterpolator where ValueType == AnimationFrameTime {
|
|
109
|
-
/// A `KeyframeInterpolator` for the given `timeRemapping` keyframes
|
|
110
|
-
static func timeRemapping(
|
|
111
|
-
keyframes timeRemappingKeyframes: KeyframeGroup<LottieVector1D>,
|
|
112
|
-
context: LayerContext)
|
|
113
|
-
throws -> KeyframeInterpolator<AnimationFrameTime>
|
|
114
|
-
{
|
|
115
|
-
try context.logCompatibilityIssue("""
|
|
116
|
-
The Core Animation rendering engine partially supports time remapping keyframes,
|
|
117
|
-
but this is somewhat experimental and has some known issues. Since it doesn't work
|
|
118
|
-
in all cases, we have to fall back to using the main thread engine when using
|
|
119
|
-
`RenderingEngineOption.automatic`.
|
|
120
|
-
""")
|
|
121
|
-
|
|
122
|
-
// `timeRemapping` is a mapping from the animation's global time to the layer's local time.
|
|
123
|
-
// In the Core Animation engine, we need to perform the opposite calculation -- convert
|
|
124
|
-
// the layer's local time into the animation's global time. We can get this by inverting
|
|
125
|
-
// the time remapping, swapping the x axis (global time) and the y axis (local time).
|
|
126
|
-
let localTimeToGlobalTimeMapping = timeRemappingKeyframes.keyframes.map { keyframe in
|
|
127
|
-
Keyframe(
|
|
128
|
-
value: keyframe.time,
|
|
129
|
-
time: keyframe.value.cgFloatValue * CGFloat(context.animation.framerate),
|
|
130
|
-
isHold: keyframe.isHold,
|
|
131
|
-
inTangent: keyframe.inTangent,
|
|
132
|
-
outTangent: keyframe.outTangent,
|
|
133
|
-
spatialInTangent: keyframe.spatialInTangent,
|
|
134
|
-
spatialOutTangent: keyframe.spatialOutTangent)
|
|
135
|
-
}
|
|
136
|
-
|
|
137
|
-
return KeyframeInterpolator(keyframes: .init(localTimeToGlobalTimeMapping))
|
|
138
|
-
}
|
|
139
|
-
}
|
|
@@ -67,7 +67,7 @@ final class ShapeItemLayer: BaseAnimationLayer {
|
|
|
67
67
|
override func setupAnimations(context: LayerAnimationContext) throws {
|
|
68
68
|
try super.setupAnimations(context: context)
|
|
69
69
|
|
|
70
|
-
guard let sublayerConfiguration
|
|
70
|
+
guard let sublayerConfiguration else { return }
|
|
71
71
|
|
|
72
72
|
switch sublayerConfiguration.fill {
|
|
73
73
|
case .solidFill(let shapeLayer):
|
|
@@ -299,7 +299,7 @@ final class ShapeItemLayer: BaseAnimationLayer {
|
|
|
299
299
|
|
|
300
300
|
// MARK: - [ShapeItem] helpers
|
|
301
301
|
|
|
302
|
-
extension
|
|
302
|
+
extension [ShapeItemLayer.Item] {
|
|
303
303
|
/// The first `ShapeItem` in this array of the given type
|
|
304
304
|
func first<ItemType: ShapeItem>(
|
|
305
305
|
_: ItemType.Type,
|