lottie-ios 4.1.1 → 4.1.3
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/Lottie.xcworkspace/xcuserdata/calstephens.xcuserdatad/UserInterfaceState.xcuserstate +0 -0
- package/README.md +4 -4
- package/Rakefile +5 -1
- package/Sources/Private/CoreAnimation/Animations/LayerProperty.swift +9 -2
- package/Sources/Private/CoreAnimation/Animations/TransformAnimations.swift +58 -9
- package/Sources/Private/CoreAnimation/Extensions/Keyframes+combined.swift +16 -12
- package/Sources/Private/CoreAnimation/Layers/BaseCompositionLayer.swift +19 -4
- package/Sources/Private/CoreAnimation/Layers/ImageLayer.swift +2 -2
- package/Sources/Private/CoreAnimation/Layers/PreCompLayer.swift +1 -1
- package/Sources/Private/CoreAnimation/Layers/RepeaterLayer.swift +13 -2
- package/Sources/Private/MainThread/LayerContainers/Utility/LayerTransformNode.swift +13 -4
- package/Sources/Private/MainThread/NodeRenderSystem/Nodes/RenderContainers/GroupNode.swift +17 -5
- package/Sources/Private/MainThread/NodeRenderSystem/Nodes/Text/TextAnimatorNode.swift +25 -6
- package/Sources/Private/Model/Objects/Transform.swift +58 -17
- package/Sources/Private/Model/ShapeItems/Repeater.swift +41 -7
- package/Sources/Private/Model/ShapeItems/ShapeTransform.swift +61 -7
- package/Sources/Private/Model/Text/TextAnimator.swift +37 -5
- package/Sources/Private/Utility/Primitives/ColorExtension.swift +10 -13
- package/Sources/Private/Utility/Primitives/VectorsExtensions.swift +28 -6
- package/Sources/Public/Animation/LottieAnimationView.swift +161 -159
- package/Sources/Public/DotLottie/DotLottieFileHelpers.swift +9 -3
- package/Sources/Public/iOS/Compatibility/CompatibleAnimationView.swift +32 -0
- package/Sources/Public/iOS/LottieAnimationViewBase.swift +1 -1
- package/Sources/Public/macOS/LottieAnimationViewBase.macOS.swift +1 -1
- package/lottie-ios.podspec +1 -1
- package/package.json +1 -1
package/Lottie.xcworkspace/xcuserdata/calstephens.xcuserdatad/UserInterfaceState.xcuserstate
CHANGED
|
Binary file
|
package/README.md
CHANGED
|
@@ -8,7 +8,7 @@ Lottie is a cross-platform library for iOS, macOS, tvOS, [Android](https://githu
|
|
|
8
8
|
Lottie loads and renders animations and vectors exported in the bodymovin JSON format. Bodymovin JSON can be created and exported from After Effects with [bodymovin](https://github.com/bodymovin/bodymovin), Sketch with [Lottie Sketch Export](https://github.com/buba447/Lottie-Sketch-Export), and from [Haiku](https://www.haiku.ai).
|
|
9
9
|
|
|
10
10
|
Designers can create **and ship** beautiful animations without an engineer painstakingly recreating them by hand.
|
|
11
|
-
Since the
|
|
11
|
+
Since the animations are backed by JSON, they are extremely small in size but can be large in complexity!
|
|
12
12
|
Animations can be played, resized, looped, sped up, slowed down, reversed, and even interactively scrubbed.
|
|
13
13
|
Lottie can play or loop just a portion of the animation as well, the possibilities are endless!
|
|
14
14
|
Animations can even be ***changed at runtime*** in various ways! Change the color, position, or any keyframable value!
|
|
@@ -41,10 +41,10 @@ To install Lottie using [Swift Package Manager](https://github.com/apple/swift-p
|
|
|
41
41
|
or you can add the following dependency to your `Package.swift`:
|
|
42
42
|
|
|
43
43
|
```swift
|
|
44
|
-
.package(url: "https://github.com/airbnb/lottie-spm.git", from: "4.1.
|
|
44
|
+
.package(url: "https://github.com/airbnb/lottie-spm.git", from: "4.1.3")
|
|
45
45
|
```
|
|
46
46
|
|
|
47
|
-
When using Swift Package Manager we recommend using the [lottie-spm](https://github.com/airbnb/lottie-spm) repo instead of the main lottie-ios repo. The main git repository for [lottie-ios](https://github.com/airbnb/lottie-ios) is somewhat large (300+ MB), and Swift Package Manager always downloads the full repository with all git
|
|
47
|
+
When using Swift Package Manager we recommend using the [lottie-spm](https://github.com/airbnb/lottie-spm) repo instead of the main lottie-ios repo. The main git repository for [lottie-ios](https://github.com/airbnb/lottie-ios) is somewhat large (300+ MB), and Swift Package Manager always downloads the full repository with all git history. The [lottie-spm](https://github.com/airbnb/lottie-spm) repo is much smaller (less than 500kb), so can be downloaded much more quickly.
|
|
48
48
|
|
|
49
49
|
Instead of downloading the full git history of Lottie and building it from source, the lottie-spm repo just contains a pointer to the precompiled XCFramework included in the [latest lottie-ios release](https://github.com/airbnb/lottie-ios/releases/latest) (typically ~8MB). If you prefer to include Lottie source directly your project, you can directly depend on the main lottie-ios repo by referencing `https://github.com/airbnb/lottie-ios.git` instead.
|
|
50
50
|
|
|
@@ -88,7 +88,7 @@ We always appreciate contributions from the community. To make changes to the pr
|
|
|
88
88
|
|
|
89
89
|
All pull requests with new features or bug fixes that affect how animations render should include snapshot test cases that validate the included changes.
|
|
90
90
|
- To add a new sample animation to the snapshot testing suite, you can add the `.json` file to `Tests/Samples`. Re-run the snapshot tests to generate the new snapshot image files.
|
|
91
|
-
- To update existing snapshots after making changes, you can set `isRecording = true` in `SnapshotTests.swift` and then re-run the snapshot tests.
|
|
91
|
+
- To update existing snapshots after making changes, you can set `isRecording = true` in `SnapshotTests.swift` `setUp()` method and then re-run the snapshot tests.
|
|
92
92
|
|
|
93
93
|
The project also includes several helpful commands defined in our [Rakefile](https://github.com/airbnb/lottie-ios/blob/master/Rakefile). To use these, you need to install [Bundler](https://bundler.io/):
|
|
94
94
|
|
package/Rakefile
CHANGED
|
@@ -49,6 +49,7 @@ namespace :build do
|
|
|
49
49
|
sh 'rm -rf .build/archives'
|
|
50
50
|
xcodebuild('archive -workspace Lottie.xcworkspace -scheme "Lottie (iOS)" -destination generic/platform=iOS -archivePath ".build/archives/Lottie_iOS" SKIP_INSTALL=NO BUILD_LIBRARY_FOR_DISTRIBUTION=YES')
|
|
51
51
|
xcodebuild('archive -workspace Lottie.xcworkspace -scheme "Lottie (iOS)" -destination "generic/platform=iOS Simulator" -archivePath ".build/archives/Lottie_iOS_Simulator" SKIP_INSTALL=NO BUILD_LIBRARY_FOR_DISTRIBUTION=YES')
|
|
52
|
+
xcodebuild('archive -workspace Lottie.xcworkspace -scheme "Lottie (iOS)" -destination "generic/platform=macOS,variant=Mac Catalyst" -archivePath ".build/archives/Lottie_Mac_Catalyst" SKIP_INSTALL=NO BUILD_LIBRARY_FOR_DISTRIBUTION=YES')
|
|
52
53
|
xcodebuild('archive -workspace Lottie.xcworkspace -scheme "Lottie (macOS)" -destination generic/platform=macOS -archivePath ".build/archives/Lottie_macOS" SKIP_INSTALL=NO BUILD_LIBRARY_FOR_DISTRIBUTION=YES')
|
|
53
54
|
xcodebuild('archive -workspace Lottie.xcworkspace -scheme "Lottie (tvOS)" -destination generic/platform=tvOS -archivePath ".build/archives/Lottie_tvOS" SKIP_INSTALL=NO BUILD_LIBRARY_FOR_DISTRIBUTION=YES')
|
|
54
55
|
xcodebuild('archive -workspace Lottie.xcworkspace -scheme "Lottie (tvOS)" -destination "generic/platform=tvOS Simulator" -archivePath ".build/archives/Lottie_tvOS_Simulator" SKIP_INSTALL=NO BUILD_LIBRARY_FOR_DISTRIBUTION=YES')
|
|
@@ -57,13 +58,16 @@ namespace :build do
|
|
|
57
58
|
'-create-xcframework',
|
|
58
59
|
'-framework .build/archives/Lottie_iOS.xcarchive/Products/Library/Frameworks/Lottie.framework',
|
|
59
60
|
'-framework .build/archives/Lottie_iOS_Simulator.xcarchive/Products/Library/Frameworks/Lottie.framework',
|
|
61
|
+
'-framework .build/archives/Lottie_Mac_Catalyst.xcarchive/Products/Library/Frameworks/Lottie.framework',
|
|
60
62
|
'-framework .build/archives/Lottie_macOS.xcarchive/Products/Library/Frameworks/Lottie.framework',
|
|
61
63
|
'-framework .build/archives/Lottie_tvOS.xcarchive/Products/Library/Frameworks/Lottie.framework',
|
|
62
64
|
'-framework .build/archives/Lottie_tvOS_Simulator.xcarchive/Products/Library/Frameworks/Lottie.framework',
|
|
63
65
|
'-output .build/archives/Lottie.xcframework'
|
|
64
66
|
].join(" "))
|
|
65
67
|
Dir.chdir('.build/archives') do
|
|
66
|
-
|
|
68
|
+
# Use --symlinks to avoid "Multiple binaries share the same codesign path. This can happen if your build process copies frameworks by following symlinks."
|
|
69
|
+
# error when validating macOS apps (#1948)
|
|
70
|
+
sh 'zip -r --symlinks Lottie.xcframework.zip Lottie.xcframework'
|
|
67
71
|
sh 'rm -rf Lottie.xcframework'
|
|
68
72
|
end
|
|
69
73
|
sh 'swift package compute-checksum .build/archives/Lottie.xcframework.zip'
|
|
@@ -109,9 +109,9 @@ extension LayerProperty {
|
|
|
109
109
|
customizableProperty: nil /* currently unsupported */ )
|
|
110
110
|
}
|
|
111
111
|
|
|
112
|
-
static var
|
|
112
|
+
static var rotationX: LayerProperty<CGFloat> {
|
|
113
113
|
.init(
|
|
114
|
-
caLayerKeypath: "transform.rotation",
|
|
114
|
+
caLayerKeypath: "transform.rotation.x",
|
|
115
115
|
defaultValue: 0,
|
|
116
116
|
customizableProperty: nil /* currently unsupported */ )
|
|
117
117
|
}
|
|
@@ -123,6 +123,13 @@ extension LayerProperty {
|
|
|
123
123
|
customizableProperty: nil /* currently unsupported */ )
|
|
124
124
|
}
|
|
125
125
|
|
|
126
|
+
static var rotationZ: LayerProperty<CGFloat> {
|
|
127
|
+
.init(
|
|
128
|
+
caLayerKeypath: "transform.rotation.z",
|
|
129
|
+
defaultValue: 0,
|
|
130
|
+
customizableProperty: nil /* currently unsupported */ )
|
|
131
|
+
}
|
|
132
|
+
|
|
126
133
|
static var anchorPoint: LayerProperty<CGPoint> {
|
|
127
134
|
.init(
|
|
128
135
|
caLayerKeypath: #keyPath(CALayer.anchorPoint),
|
|
@@ -24,8 +24,14 @@ protocol TransformModel {
|
|
|
24
24
|
/// The scale of the transform
|
|
25
25
|
var scale: KeyframeGroup<LottieVector3D> { get }
|
|
26
26
|
|
|
27
|
-
/// The rotation of the transform
|
|
28
|
-
var
|
|
27
|
+
/// The rotation of the transform on X axis.
|
|
28
|
+
var rotationX: KeyframeGroup<LottieVector1D> { get }
|
|
29
|
+
|
|
30
|
+
/// The rotation of the transform on Y axis.
|
|
31
|
+
var rotationY: KeyframeGroup<LottieVector1D> { get }
|
|
32
|
+
|
|
33
|
+
/// The rotation of the transform on Z axis.
|
|
34
|
+
var rotationZ: KeyframeGroup<LottieVector1D> { get }
|
|
29
35
|
}
|
|
30
36
|
|
|
31
37
|
// MARK: - Transform + TransformModel
|
|
@@ -75,7 +81,7 @@ extension CALayer {
|
|
|
75
81
|
try addPositionAnimations(from: transformModel, context: context)
|
|
76
82
|
try addAnchorPointAnimation(from: transformModel, context: context)
|
|
77
83
|
try addScaleAnimations(from: transformModel, context: context)
|
|
78
|
-
try
|
|
84
|
+
try addRotationAnimations(from: transformModel, context: context)
|
|
79
85
|
}
|
|
80
86
|
}
|
|
81
87
|
|
|
@@ -227,14 +233,53 @@ extension CALayer {
|
|
|
227
233
|
context: context)
|
|
228
234
|
}
|
|
229
235
|
|
|
230
|
-
private func
|
|
236
|
+
private func addRotationAnimations(
|
|
231
237
|
from transformModel: TransformModel,
|
|
232
238
|
context: LayerAnimationContext)
|
|
233
239
|
throws
|
|
234
240
|
{
|
|
241
|
+
let containsXRotationValues = transformModel.rotationX.keyframes.contains(where: { $0.value.cgFloatValue != 0 })
|
|
242
|
+
let containsYRotationValues = transformModel.rotationY.keyframes.contains(where: { $0.value.cgFloatValue != 0 })
|
|
243
|
+
|
|
244
|
+
// When `rotation.x` or `rotation.y` is used, it doesn't render property in test snapshots
|
|
245
|
+
// but do renders correctly on the simulator / device
|
|
246
|
+
if TestHelpers.snapshotTestsAreRunning {
|
|
247
|
+
if containsXRotationValues {
|
|
248
|
+
context.logger.warn("""
|
|
249
|
+
`rotation.x` values are not displayed correctly in snapshot tests
|
|
250
|
+
""")
|
|
251
|
+
}
|
|
252
|
+
|
|
253
|
+
if containsYRotationValues {
|
|
254
|
+
context.logger.warn("""
|
|
255
|
+
`rotation.y` values are not displayed correctly in snapshot tests
|
|
256
|
+
""")
|
|
257
|
+
}
|
|
258
|
+
}
|
|
259
|
+
|
|
260
|
+
// Lottie animation files express rotation in degrees
|
|
261
|
+
// (e.g. 90º, 180º, 360º) so we covert to radians to get the
|
|
262
|
+
// values expected by Core Animation (e.g. π/2, π, 2π)
|
|
263
|
+
|
|
264
|
+
try addAnimation(
|
|
265
|
+
for: .rotationX,
|
|
266
|
+
keyframes: transformModel.rotationX.keyframes,
|
|
267
|
+
value: { rotationDegrees in
|
|
268
|
+
rotationDegrees.cgFloatValue * .pi / 180
|
|
269
|
+
},
|
|
270
|
+
context: context)
|
|
271
|
+
|
|
272
|
+
try addAnimation(
|
|
273
|
+
for: .rotationY,
|
|
274
|
+
keyframes: transformModel.rotationY.keyframes,
|
|
275
|
+
value: { rotationDegrees in
|
|
276
|
+
rotationDegrees.cgFloatValue * .pi / 180
|
|
277
|
+
},
|
|
278
|
+
context: context)
|
|
279
|
+
|
|
235
280
|
try addAnimation(
|
|
236
|
-
for: .
|
|
237
|
-
keyframes: transformModel.
|
|
281
|
+
for: .rotationZ,
|
|
282
|
+
keyframes: transformModel.rotationZ.keyframes,
|
|
238
283
|
value: { rotationDegrees in
|
|
239
284
|
// Lottie animation files express rotation in degrees
|
|
240
285
|
// (e.g. 90º, 180º, 360º) so we covert to radians to get the
|
|
@@ -257,15 +302,19 @@ extension CALayer {
|
|
|
257
302
|
transformModel.anchor,
|
|
258
303
|
transformModel.position,
|
|
259
304
|
transformModel.scale,
|
|
260
|
-
transformModel.
|
|
305
|
+
transformModel.rotationX,
|
|
306
|
+
transformModel.rotationY,
|
|
307
|
+
transformModel.rotationZ,
|
|
261
308
|
transformModel.skew,
|
|
262
309
|
transformModel.skewAxis,
|
|
263
|
-
makeCombinedResult: { anchor, position, scale,
|
|
310
|
+
makeCombinedResult: { anchor, position, scale, rotationX, rotationY, rotationZ, skew, skewAxis in
|
|
264
311
|
CATransform3D.makeTransform(
|
|
265
312
|
anchor: anchor.pointValue,
|
|
266
313
|
position: position.pointValue,
|
|
267
314
|
scale: scale.sizeValue,
|
|
268
|
-
|
|
315
|
+
rotationX: rotationX.cgFloatValue,
|
|
316
|
+
rotationY: rotationY.cgFloatValue,
|
|
317
|
+
rotationZ: rotationZ.cgFloatValue,
|
|
269
318
|
skew: skew.cgFloatValue,
|
|
270
319
|
skewAxis: skewAxis.cgFloatValue)
|
|
271
320
|
})
|
|
@@ -68,20 +68,21 @@ enum Keyframes {
|
|
|
68
68
|
/// Combines the given keyframe groups of `Keyframe<T>`s into a single keyframe group of of `Keyframe<[T]>`s
|
|
69
69
|
/// - If all of the `KeyframeGroup`s have the exact same animation timing, the keyframes are merged
|
|
70
70
|
/// - Otherwise, the keyframes are manually interpolated at each frame in the animation
|
|
71
|
-
static func combined<T1, T2, T3, T4, T5, T6, CombinedResult>(
|
|
71
|
+
static func combined<T1, T2, T3, T4, T5, T6, T7, CombinedResult>(
|
|
72
72
|
_ k1: KeyframeGroup<T1>,
|
|
73
73
|
_ k2: KeyframeGroup<T2>,
|
|
74
74
|
_ k3: KeyframeGroup<T3>,
|
|
75
75
|
_ k4: KeyframeGroup<T4>,
|
|
76
76
|
_ k5: KeyframeGroup<T5>,
|
|
77
77
|
_ k6: KeyframeGroup<T6>,
|
|
78
|
-
|
|
78
|
+
_ k7: KeyframeGroup<T7>,
|
|
79
|
+
makeCombinedResult: (T1, T2, T3, T4, T5, T6, T7) -> CombinedResult)
|
|
79
80
|
-> KeyframeGroup<CombinedResult>
|
|
80
81
|
where T1: AnyInterpolatable, T2: AnyInterpolatable, T3: AnyInterpolatable, T4: AnyInterpolatable,
|
|
81
|
-
T5: AnyInterpolatable, T6: AnyInterpolatable
|
|
82
|
+
T5: AnyInterpolatable, T6: AnyInterpolatable, T7: AnyInterpolatable
|
|
82
83
|
{
|
|
83
84
|
Keyframes.combined(
|
|
84
|
-
[k1, k2, k3, k4, k5, k6],
|
|
85
|
+
[k1, k2, k3, k4, k5, k6, k7],
|
|
85
86
|
makeCombinedResult: { untypedValues in
|
|
86
87
|
guard
|
|
87
88
|
let t1 = untypedValues[0] as? T1,
|
|
@@ -89,17 +90,18 @@ enum Keyframes {
|
|
|
89
90
|
let t3 = untypedValues[2] as? T3,
|
|
90
91
|
let t4 = untypedValues[3] as? T4,
|
|
91
92
|
let t5 = untypedValues[4] as? T5,
|
|
92
|
-
let t6 = untypedValues[5] as? T6
|
|
93
|
+
let t6 = untypedValues[5] as? T6,
|
|
94
|
+
let t7 = untypedValues[6] as? T7
|
|
93
95
|
else { return nil }
|
|
94
96
|
|
|
95
|
-
return makeCombinedResult(t1, t2, t3, t4, t5, t6)
|
|
97
|
+
return makeCombinedResult(t1, t2, t3, t4, t5, t6, t7)
|
|
96
98
|
})
|
|
97
99
|
}
|
|
98
100
|
|
|
99
101
|
/// Combines the given keyframe groups of `Keyframe<T>`s into a single keyframe group of of `Keyframe<[T]>`s
|
|
100
102
|
/// - If all of the `KeyframeGroup`s have the exact same animation timing, the keyframes are merged
|
|
101
103
|
/// - Otherwise, the keyframes are manually interpolated at each frame in the animation
|
|
102
|
-
static func combined<T1, T2, T3, T4, T5, T6, T7, CombinedResult>(
|
|
104
|
+
static func combined<T1, T2, T3, T4, T5, T6, T7, T8, CombinedResult>(
|
|
103
105
|
_ k1: KeyframeGroup<T1>,
|
|
104
106
|
_ k2: KeyframeGroup<T2>,
|
|
105
107
|
_ k3: KeyframeGroup<T3>,
|
|
@@ -107,13 +109,14 @@ enum Keyframes {
|
|
|
107
109
|
_ k5: KeyframeGroup<T5>,
|
|
108
110
|
_ k6: KeyframeGroup<T6>,
|
|
109
111
|
_ k7: KeyframeGroup<T7>,
|
|
110
|
-
|
|
112
|
+
_ k8: KeyframeGroup<T8>,
|
|
113
|
+
makeCombinedResult: (T1, T2, T3, T4, T5, T6, T7, T8) -> CombinedResult)
|
|
111
114
|
-> KeyframeGroup<CombinedResult>
|
|
112
115
|
where T1: AnyInterpolatable, T2: AnyInterpolatable, T3: AnyInterpolatable, T4: AnyInterpolatable,
|
|
113
|
-
T5: AnyInterpolatable, T6: AnyInterpolatable, T7: AnyInterpolatable
|
|
116
|
+
T5: AnyInterpolatable, T6: AnyInterpolatable, T7: AnyInterpolatable, T8: AnyInterpolatable
|
|
114
117
|
{
|
|
115
118
|
Keyframes.combined(
|
|
116
|
-
[k1, k2, k3, k4, k5, k6, k7],
|
|
119
|
+
[k1, k2, k3, k4, k5, k6, k7, k8],
|
|
117
120
|
makeCombinedResult: { untypedValues in
|
|
118
121
|
guard
|
|
119
122
|
let t1 = untypedValues[0] as? T1,
|
|
@@ -122,10 +125,11 @@ enum Keyframes {
|
|
|
122
125
|
let t4 = untypedValues[3] as? T4,
|
|
123
126
|
let t5 = untypedValues[4] as? T5,
|
|
124
127
|
let t6 = untypedValues[5] as? T6,
|
|
125
|
-
let t7 = untypedValues[6] as? T7
|
|
128
|
+
let t7 = untypedValues[6] as? T7,
|
|
129
|
+
let t8 = untypedValues[7] as? T8
|
|
126
130
|
else { return nil }
|
|
127
131
|
|
|
128
|
-
return makeCombinedResult(t1, t2, t3, t4, t5, t6, t7)
|
|
132
|
+
return makeCombinedResult(t1, t2, t3, t4, t5, t6, t7, t8)
|
|
129
133
|
})
|
|
130
134
|
}
|
|
131
135
|
|
|
@@ -17,6 +17,7 @@ class BaseCompositionLayer: BaseAnimationLayer {
|
|
|
17
17
|
setupSublayers()
|
|
18
18
|
compositingFilter = layerModel.blendMode.filterName
|
|
19
19
|
name = layerModel.name
|
|
20
|
+
contentsLayer.name = "\(layerModel.name) (Content)"
|
|
20
21
|
}
|
|
21
22
|
|
|
22
23
|
required init?(coder _: NSCoder) {
|
|
@@ -36,6 +37,10 @@ class BaseCompositionLayer: BaseAnimationLayer {
|
|
|
36
37
|
|
|
37
38
|
// MARK: Internal
|
|
38
39
|
|
|
40
|
+
/// The layer that content / sublayers should be rendered in.
|
|
41
|
+
/// This is the layer that transform animations are applied to.
|
|
42
|
+
let contentsLayer = BaseAnimationLayer()
|
|
43
|
+
|
|
39
44
|
/// Whether or not this layer render should render any visible content
|
|
40
45
|
var renderLayerContents: Bool { true }
|
|
41
46
|
|
|
@@ -55,12 +60,12 @@ class BaseCompositionLayer: BaseAnimationLayer {
|
|
|
55
60
|
func setupLayerAnimations(context: LayerAnimationContext) throws {
|
|
56
61
|
let context = context.addingKeypathComponent(baseLayerModel.name)
|
|
57
62
|
|
|
58
|
-
try addTransformAnimations(for: baseLayerModel.transform, context: context)
|
|
63
|
+
try contentsLayer.addTransformAnimations(for: baseLayerModel.transform, context: context)
|
|
59
64
|
|
|
60
65
|
if renderLayerContents {
|
|
61
|
-
try addOpacityAnimation(for: baseLayerModel.transform, context: context)
|
|
66
|
+
try contentsLayer.addOpacityAnimation(for: baseLayerModel.transform, context: context)
|
|
62
67
|
|
|
63
|
-
addVisibilityAnimation(
|
|
68
|
+
contentsLayer.addVisibilityAnimation(
|
|
64
69
|
inFrame: CGFloat(baseLayerModel.inFrame),
|
|
65
70
|
outFrame: CGFloat(baseLayerModel.outFrame),
|
|
66
71
|
context: context)
|
|
@@ -71,17 +76,27 @@ class BaseCompositionLayer: BaseAnimationLayer {
|
|
|
71
76
|
try super.setupAnimations(context: context)
|
|
72
77
|
}
|
|
73
78
|
|
|
79
|
+
override func addSublayer(_ layer: CALayer) {
|
|
80
|
+
if layer === contentsLayer {
|
|
81
|
+
super.addSublayer(contentsLayer)
|
|
82
|
+
} else {
|
|
83
|
+
contentsLayer.addSublayer(layer)
|
|
84
|
+
}
|
|
85
|
+
}
|
|
86
|
+
|
|
74
87
|
// MARK: Private
|
|
75
88
|
|
|
76
89
|
private let baseLayerModel: LayerModel
|
|
77
90
|
|
|
78
91
|
private func setupSublayers() {
|
|
92
|
+
addSublayer(contentsLayer)
|
|
93
|
+
|
|
79
94
|
if
|
|
80
95
|
renderLayerContents,
|
|
81
96
|
let masks = baseLayerModel.masks?.filter({ $0.mode != .none }),
|
|
82
97
|
!masks.isEmpty
|
|
83
98
|
{
|
|
84
|
-
mask = MaskCompositionLayer(masks: masks)
|
|
99
|
+
contentsLayer.mask = MaskCompositionLayer(masks: masks)
|
|
85
100
|
}
|
|
86
101
|
}
|
|
87
102
|
|
|
@@ -42,12 +42,12 @@ final class ImageLayer: BaseCompositionLayer {
|
|
|
42
42
|
let image = context.imageProvider.imageForAsset(asset: imageAsset)
|
|
43
43
|
else {
|
|
44
44
|
self.imageAsset = nil
|
|
45
|
-
contents = nil
|
|
45
|
+
contentsLayer.contents = nil
|
|
46
46
|
return
|
|
47
47
|
}
|
|
48
48
|
|
|
49
49
|
self.imageAsset = imageAsset
|
|
50
|
-
contents = image
|
|
50
|
+
contentsLayer.contents = image
|
|
51
51
|
setNeedsLayout()
|
|
52
52
|
}
|
|
53
53
|
|
|
@@ -55,7 +55,15 @@ private struct RepeaterTransform {
|
|
|
55
55
|
anchorPoint = repeater.anchorPoint
|
|
56
56
|
scale = repeater.scale
|
|
57
57
|
|
|
58
|
-
|
|
58
|
+
rotationX = repeater.rotationX.map { rotation in
|
|
59
|
+
LottieVector1D(rotation.value * Double(index))
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
rotationY = repeater.rotationY.map { rotation in
|
|
63
|
+
LottieVector1D(rotation.value * Double(index))
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
rotationZ = repeater.rotationZ.map { rotation in
|
|
59
67
|
LottieVector1D(rotation.value * Double(index))
|
|
60
68
|
}
|
|
61
69
|
|
|
@@ -71,7 +79,10 @@ private struct RepeaterTransform {
|
|
|
71
79
|
|
|
72
80
|
let anchorPoint: KeyframeGroup<LottieVector3D>
|
|
73
81
|
let position: KeyframeGroup<LottieVector3D>
|
|
74
|
-
let
|
|
82
|
+
let rotationX: KeyframeGroup<LottieVector1D>
|
|
83
|
+
let rotationY: KeyframeGroup<LottieVector1D>
|
|
84
|
+
let rotationZ: KeyframeGroup<LottieVector1D>
|
|
85
|
+
|
|
75
86
|
let scale: KeyframeGroup<LottieVector3D>
|
|
76
87
|
|
|
77
88
|
}
|
|
@@ -18,13 +18,18 @@ final class LayerTransformProperties: NodePropertyMap, KeypathSearchable {
|
|
|
18
18
|
init(transform: Transform) {
|
|
19
19
|
anchor = NodeProperty(provider: KeyframeInterpolator(keyframes: transform.anchorPoint.keyframes))
|
|
20
20
|
scale = NodeProperty(provider: KeyframeInterpolator(keyframes: transform.scale.keyframes))
|
|
21
|
-
|
|
21
|
+
rotationX = NodeProperty(provider: KeyframeInterpolator(keyframes: transform.rotationX.keyframes))
|
|
22
|
+
rotationY = NodeProperty(provider: KeyframeInterpolator(keyframes: transform.rotationY.keyframes))
|
|
23
|
+
rotationZ = NodeProperty(provider: KeyframeInterpolator(keyframes: transform.rotationZ.keyframes))
|
|
22
24
|
opacity = NodeProperty(provider: KeyframeInterpolator(keyframes: transform.opacity.keyframes))
|
|
23
25
|
|
|
24
26
|
var propertyMap: [String: AnyNodeProperty] = [
|
|
25
27
|
"Anchor Point" : anchor,
|
|
26
28
|
"Scale" : scale,
|
|
27
|
-
"Rotation"
|
|
29
|
+
"Rotation": rotationZ,
|
|
30
|
+
"Rotation X" : rotationX,
|
|
31
|
+
"Rotation Y" : rotationY,
|
|
32
|
+
"Rotation Z" : rotationZ,
|
|
28
33
|
"Opacity" : opacity,
|
|
29
34
|
]
|
|
30
35
|
|
|
@@ -64,7 +69,9 @@ final class LayerTransformProperties: NodePropertyMap, KeypathSearchable {
|
|
|
64
69
|
|
|
65
70
|
let anchor: NodeProperty<LottieVector3D>
|
|
66
71
|
let scale: NodeProperty<LottieVector3D>
|
|
67
|
-
let
|
|
72
|
+
let rotationX: NodeProperty<LottieVector1D>
|
|
73
|
+
let rotationY: NodeProperty<LottieVector1D>
|
|
74
|
+
let rotationZ: NodeProperty<LottieVector1D>
|
|
68
75
|
let position: NodeProperty<LottieVector3D>?
|
|
69
76
|
let positionX: NodeProperty<LottieVector1D>?
|
|
70
77
|
let positionY: NodeProperty<LottieVector1D>?
|
|
@@ -130,7 +137,9 @@ class LayerTransformNode: AnimatorNode {
|
|
|
130
137
|
anchor: transformProperties.anchor.value.pointValue,
|
|
131
138
|
position: position,
|
|
132
139
|
scale: transformProperties.scale.value.sizeValue,
|
|
133
|
-
|
|
140
|
+
rotationX: transformProperties.rotationX.value.cgFloatValue,
|
|
141
|
+
rotationY: transformProperties.rotationY.value.cgFloatValue,
|
|
142
|
+
rotationZ: transformProperties.rotationZ.value.cgFloatValue,
|
|
134
143
|
skew: nil,
|
|
135
144
|
skewAxis: nil)
|
|
136
145
|
|
|
@@ -20,7 +20,9 @@ final class GroupNodeProperties: NodePropertyMap, KeypathSearchable {
|
|
|
20
20
|
anchor = NodeProperty(provider: KeyframeInterpolator(keyframes: transform.anchor.keyframes))
|
|
21
21
|
position = NodeProperty(provider: KeyframeInterpolator(keyframes: transform.position.keyframes))
|
|
22
22
|
scale = NodeProperty(provider: KeyframeInterpolator(keyframes: transform.scale.keyframes))
|
|
23
|
-
|
|
23
|
+
rotationX = NodeProperty(provider: KeyframeInterpolator(keyframes: transform.rotationX.keyframes))
|
|
24
|
+
rotationY = NodeProperty(provider: KeyframeInterpolator(keyframes: transform.rotationY.keyframes))
|
|
25
|
+
rotationZ = NodeProperty(provider: KeyframeInterpolator(keyframes: transform.rotationZ.keyframes))
|
|
24
26
|
opacity = NodeProperty(provider: KeyframeInterpolator(keyframes: transform.opacity.keyframes))
|
|
25
27
|
skew = NodeProperty(provider: KeyframeInterpolator(keyframes: transform.skew.keyframes))
|
|
26
28
|
skewAxis = NodeProperty(provider: KeyframeInterpolator(keyframes: transform.skewAxis.keyframes))
|
|
@@ -29,7 +31,9 @@ final class GroupNodeProperties: NodePropertyMap, KeypathSearchable {
|
|
|
29
31
|
anchor = NodeProperty(provider: SingleValueProvider(LottieVector3D(x: CGFloat(0), y: CGFloat(0), z: CGFloat(0))))
|
|
30
32
|
position = NodeProperty(provider: SingleValueProvider(LottieVector3D(x: CGFloat(0), y: CGFloat(0), z: CGFloat(0))))
|
|
31
33
|
scale = NodeProperty(provider: SingleValueProvider(LottieVector3D(x: CGFloat(100), y: CGFloat(100), z: CGFloat(100))))
|
|
32
|
-
|
|
34
|
+
rotationX = NodeProperty(provider: SingleValueProvider(LottieVector1D(0)))
|
|
35
|
+
rotationY = NodeProperty(provider: SingleValueProvider(LottieVector1D(0)))
|
|
36
|
+
rotationZ = NodeProperty(provider: SingleValueProvider(LottieVector1D(0)))
|
|
33
37
|
opacity = NodeProperty(provider: SingleValueProvider(LottieVector1D(100)))
|
|
34
38
|
skew = NodeProperty(provider: SingleValueProvider(LottieVector1D(0)))
|
|
35
39
|
skewAxis = NodeProperty(provider: SingleValueProvider(LottieVector1D(0)))
|
|
@@ -38,7 +42,10 @@ final class GroupNodeProperties: NodePropertyMap, KeypathSearchable {
|
|
|
38
42
|
"Anchor Point" : anchor,
|
|
39
43
|
"Position" : position,
|
|
40
44
|
"Scale" : scale,
|
|
41
|
-
"Rotation" :
|
|
45
|
+
"Rotation" : rotationZ,
|
|
46
|
+
"Rotation X" : rotationX,
|
|
47
|
+
"Rotation Y" : rotationY,
|
|
48
|
+
"Rotation Z" : rotationZ,
|
|
42
49
|
"Opacity" : opacity,
|
|
43
50
|
"Skew" : skew,
|
|
44
51
|
"Skew Axis" : skewAxis,
|
|
@@ -58,7 +65,10 @@ final class GroupNodeProperties: NodePropertyMap, KeypathSearchable {
|
|
|
58
65
|
let anchor: NodeProperty<LottieVector3D>
|
|
59
66
|
let position: NodeProperty<LottieVector3D>
|
|
60
67
|
let scale: NodeProperty<LottieVector3D>
|
|
61
|
-
let
|
|
68
|
+
let rotationX: NodeProperty<LottieVector1D>
|
|
69
|
+
let rotationY: NodeProperty<LottieVector1D>
|
|
70
|
+
let rotationZ: NodeProperty<LottieVector1D>
|
|
71
|
+
|
|
62
72
|
let opacity: NodeProperty<LottieVector1D>
|
|
63
73
|
let skew: NodeProperty<LottieVector1D>
|
|
64
74
|
let skewAxis: NodeProperty<LottieVector1D>
|
|
@@ -68,7 +78,9 @@ final class GroupNodeProperties: NodePropertyMap, KeypathSearchable {
|
|
|
68
78
|
anchor: anchor.value.pointValue,
|
|
69
79
|
position: position.value.pointValue,
|
|
70
80
|
scale: scale.value.sizeValue,
|
|
71
|
-
|
|
81
|
+
rotationX: rotationX.value.cgFloatValue,
|
|
82
|
+
rotationY: rotationY.value.cgFloatValue,
|
|
83
|
+
rotationZ: rotationZ.value.cgFloatValue,
|
|
72
84
|
skew: skew.value.cgFloatValue,
|
|
73
85
|
skewAxis: skewAxis.value.cgFloatValue)
|
|
74
86
|
}
|
|
@@ -54,11 +54,26 @@ final class TextAnimatorNodeProperties: NodePropertyMap, KeypathSearchable {
|
|
|
54
54
|
skewAxis = nil
|
|
55
55
|
}
|
|
56
56
|
|
|
57
|
-
if let keyframeGroup = textAnimator.
|
|
58
|
-
|
|
59
|
-
properties["Rotation"] =
|
|
57
|
+
if let keyframeGroup = textAnimator.rotationX {
|
|
58
|
+
rotationX = NodeProperty(provider: KeyframeInterpolator(keyframes: keyframeGroup.keyframes))
|
|
59
|
+
properties["Rotation X"] = rotationX
|
|
60
60
|
} else {
|
|
61
|
-
|
|
61
|
+
rotationX = nil
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
if let keyframeGroup = textAnimator.rotationY {
|
|
65
|
+
rotationY = NodeProperty(provider: KeyframeInterpolator(keyframes: keyframeGroup.keyframes))
|
|
66
|
+
properties["Rotation Y"] = rotationY
|
|
67
|
+
} else {
|
|
68
|
+
rotationY = nil
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
if let keyframeGroup = textAnimator.rotationZ {
|
|
72
|
+
rotationZ = NodeProperty(provider: KeyframeInterpolator(keyframes: keyframeGroup.keyframes))
|
|
73
|
+
properties["Rotation Z"] = rotationZ
|
|
74
|
+
properties["Rotation"] = rotationZ
|
|
75
|
+
} else {
|
|
76
|
+
rotationZ = nil
|
|
62
77
|
}
|
|
63
78
|
|
|
64
79
|
if let keyframeGroup = textAnimator.opacity {
|
|
@@ -110,7 +125,9 @@ final class TextAnimatorNodeProperties: NodePropertyMap, KeypathSearchable {
|
|
|
110
125
|
let scale: NodeProperty<LottieVector3D>?
|
|
111
126
|
let skew: NodeProperty<LottieVector1D>?
|
|
112
127
|
let skewAxis: NodeProperty<LottieVector1D>?
|
|
113
|
-
let
|
|
128
|
+
let rotationX: NodeProperty<LottieVector1D>?
|
|
129
|
+
let rotationY: NodeProperty<LottieVector1D>?
|
|
130
|
+
let rotationZ: NodeProperty<LottieVector1D>?
|
|
114
131
|
let opacity: NodeProperty<LottieVector1D>?
|
|
115
132
|
let strokeColor: NodeProperty<LottieColor>?
|
|
116
133
|
let fillColor: NodeProperty<LottieColor>?
|
|
@@ -125,7 +142,9 @@ final class TextAnimatorNodeProperties: NodePropertyMap, KeypathSearchable {
|
|
|
125
142
|
anchor: anchor?.value.pointValue ?? .zero,
|
|
126
143
|
position: position?.value.pointValue ?? .zero,
|
|
127
144
|
scale: scale?.value.sizeValue ?? CGSize(width: 100, height: 100),
|
|
128
|
-
|
|
145
|
+
rotationX: rotationX?.value.cgFloatValue ?? 0,
|
|
146
|
+
rotationY: rotationY?.value.cgFloatValue ?? 0,
|
|
147
|
+
rotationZ: rotationZ?.value.cgFloatValue ?? 0,
|
|
129
148
|
skew: skew?.value.cgFloatValue,
|
|
130
149
|
skewAxis: skewAxis?.value.cgFloatValue)
|
|
131
150
|
}
|