lottie-ios 4.1.2 → 4.2.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 +27 -9
- package/Lottie.xcodeproj/project.pbxproj +158 -70
- package/Lottie.xcodeproj/xcuserdata/calstephens.xcuserdatad/xcschemes/xcschememanagement.plist +2 -2
- package/Lottie.xcworkspace/xcuserdata/calstephens.xcuserdatad/IDEFindNavigatorScopes.plist +5 -0
- package/Lottie.xcworkspace/xcuserdata/calstephens.xcuserdatad/UserInterfaceState.xcuserstate +0 -0
- package/Lottie.xcworkspace/xcuserdata/calstephens.xcuserdatad/xcdebugger/Breakpoints_v2.xcbkptlist +258 -0
- package/Lottie.xcworkspace/xcuserdata/calstephens.xcuserdatad/xcdebugger/Expressions.xcexplist +13 -2
- package/Package.swift +2 -1
- package/README.md +3 -3
- package/Rakefile +8 -4
- package/Sources/Private/CoreAnimation/Animations/CALayer+addAnimation.swift +16 -2
- package/Sources/Private/CoreAnimation/Animations/CombinedShapeAnimation.swift +1 -1
- package/Sources/Private/CoreAnimation/Animations/CustomPathAnimation.swift +1 -1
- package/Sources/Private/CoreAnimation/Animations/EllipseAnimation.swift +1 -1
- package/Sources/Private/CoreAnimation/Animations/GradientAnimations.swift +6 -6
- package/Sources/Private/CoreAnimation/Animations/LayerProperty.swift +76 -7
- package/Sources/Private/CoreAnimation/Animations/OpacityAnimation.swift +1 -1
- package/Sources/Private/CoreAnimation/Animations/RectangleAnimation.swift +1 -1
- package/Sources/Private/CoreAnimation/Animations/ShapeAnimation.swift +66 -102
- package/Sources/Private/CoreAnimation/Animations/StarAnimation.swift +2 -2
- package/Sources/Private/CoreAnimation/Animations/StrokeAnimation.swift +3 -3
- package/Sources/Private/CoreAnimation/Animations/TransformAnimations.swift +66 -17
- package/Sources/Private/CoreAnimation/CoreAnimationLayer.swift +55 -32
- package/Sources/Private/CoreAnimation/Extensions/Keyframes+combined.swift +16 -12
- package/Sources/Private/CoreAnimation/Layers/AnimationLayer.swift +3 -3
- package/Sources/Private/CoreAnimation/Layers/BaseCompositionLayer.swift +24 -11
- 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/CoreAnimation/Layers/ShapeLayer.swift +9 -1
- package/Sources/Private/CoreAnimation/ValueProviderStore.swift +22 -11
- package/Sources/Private/MainThread/LayerContainers/CompLayers/MaskContainerLayer.swift +1 -1
- package/Sources/Private/MainThread/LayerContainers/MainThreadAnimationLayer.swift +13 -2
- package/Sources/Private/MainThread/LayerContainers/Utility/LayerTransformNode.swift +16 -7
- package/Sources/Private/MainThread/NodeRenderSystem/Nodes/PathNodes/EllipseNode.swift +1 -1
- package/Sources/Private/MainThread/NodeRenderSystem/Nodes/PathNodes/PolygonNode.swift +2 -2
- package/Sources/Private/MainThread/NodeRenderSystem/Nodes/PathNodes/RectNode.swift +1 -1
- package/Sources/Private/MainThread/NodeRenderSystem/Nodes/PathNodes/StarNode.swift +2 -2
- package/Sources/Private/MainThread/NodeRenderSystem/Nodes/RenderContainers/GroupNode.swift +20 -8
- package/Sources/Private/MainThread/NodeRenderSystem/Nodes/RenderNodes/FillNode.swift +1 -1
- package/Sources/Private/MainThread/NodeRenderSystem/Nodes/RenderNodes/GradientFillNode.swift +1 -1
- package/Sources/Private/MainThread/NodeRenderSystem/Nodes/RenderNodes/GradientStrokeNode.swift +1 -1
- package/Sources/Private/MainThread/NodeRenderSystem/Nodes/RenderNodes/StrokeNode.swift +1 -1
- package/Sources/Private/MainThread/NodeRenderSystem/Nodes/Text/TextAnimatorNode.swift +28 -9
- package/Sources/Private/Model/Assets/ImageAsset.swift +4 -3
- package/Sources/Private/Model/DotLottie/DotLottieAnimation.swift +2 -8
- package/Sources/Private/Model/DotLottie/DotLottieManifest.swift +3 -14
- package/Sources/Private/Model/DotLottie/DotLottieUtils.swift +11 -1
- package/Sources/Private/Model/DotLottie/ZipFoundation/Archive+BackingConfiguration.swift +147 -0
- package/Sources/Private/Model/DotLottie/ZipFoundation/Archive+Helpers.swift +351 -0
- package/Sources/Private/Model/DotLottie/ZipFoundation/Archive+MemoryFile.swift +183 -0
- package/Sources/Private/Model/DotLottie/ZipFoundation/Archive+Progress.swift +66 -0
- package/Sources/Private/Model/DotLottie/ZipFoundation/Archive+Reading.swift +144 -0
- package/Sources/Private/Model/DotLottie/ZipFoundation/Archive+ReadingDeprecated.swift +49 -0
- package/Sources/Private/Model/DotLottie/ZipFoundation/Archive+Writing.swift +385 -0
- package/Sources/Private/Model/DotLottie/ZipFoundation/Archive+WritingDeprecated.swift +91 -0
- package/Sources/Private/Model/DotLottie/ZipFoundation/Archive+ZIP64.swift +170 -0
- package/Sources/Private/Model/DotLottie/{Zip/ZipArchive.swift → ZipFoundation/Archive.swift} +150 -227
- package/Sources/Private/Model/DotLottie/ZipFoundation/Data+Compression.swift +403 -0
- package/Sources/Private/Model/DotLottie/ZipFoundation/Data+CompressionDeprecated.swift +44 -0
- package/Sources/Private/Model/DotLottie/{Zip → ZipFoundation}/Data+Serialization.swift +62 -0
- package/Sources/Private/Model/DotLottie/{Zip/ZipEntry+Serialization.swift → ZipFoundation/Entry+Serialization.swift} +7 -7
- package/Sources/Private/Model/DotLottie/{Zip/ZipEntry+ZIP64.swift → ZipFoundation/Entry+ZIP64.swift} +13 -19
- package/Sources/Private/Model/DotLottie/{Zip/ZipEntry.swift → ZipFoundation/Entry.swift} +141 -10
- package/Sources/Private/Model/DotLottie/ZipFoundation/FileManager+ZIP.swift +368 -0
- package/Sources/Private/Model/DotLottie/ZipFoundation/README.md +24 -0
- package/Sources/Private/Model/DotLottie/ZipFoundation/URL+ZIP.swift +32 -0
- package/Sources/Private/Model/Extensions/Bundle.swift +5 -14
- package/Sources/Private/Model/Keyframes/KeyframeGroup.swift +31 -8
- 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/RootAnimationLayer.swift +3 -1
- package/Sources/Private/Utility/Extensions/AnimationKeypathExtension.swift +12 -4
- package/Sources/Private/Utility/Extensions/DataExtension.swift +14 -4
- package/Sources/Private/Utility/Primitives/BezierPathRoundExtension.swift +11 -0
- package/Sources/Private/Utility/Primitives/ColorExtension.swift +10 -13
- package/Sources/Private/Utility/Primitives/VectorsExtensions.swift +28 -6
- package/Sources/Public/Animation/LottieAnimationHelpers.swift +12 -10
- package/Sources/Public/Animation/LottieAnimationView.swift +213 -186
- package/Sources/Public/DotLottie/DotLottieFile.swift +11 -34
- package/Sources/Public/DotLottie/DotLottieFileHelpers.swift +101 -74
- package/Sources/Public/iOS/Compatibility/CompatibleAnimationView.swift +90 -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/LottieAnimation/LottieAnimation.xcodeproj/project.xcworkspace/xcuserdata/calstephens.xcuserdatad/UserInterfaceState.xcuserstate +0 -0
- package/LottieAnimation/LottieAnimation.xcodeproj/project.xcworkspace/xcuserdata/valentinperignon.xcuserdatad/UserInterfaceState.xcuserstate +0 -0
- package/LottieAnimation/LottieAnimation.xcodeproj/xcuserdata/calstephens.xcuserdatad/xcschemes/xcschememanagement.plist +0 -14
- package/LottieAnimation/LottieAnimation.xcodeproj/xcuserdata/valentinperignon.xcuserdatad/xcdebugger/Breakpoints_v2.xcbkptlist +0 -6
- package/LottieAnimation/LottieAnimation.xcodeproj/xcuserdata/valentinperignon.xcuserdatad/xcschemes/xcschememanagement.plist +0 -14
- package/Sources/Private/Model/DotLottie/Zip/Data+Compression.swift +0 -134
- package/Sources/Private/Model/DotLottie/Zip/FileManager+ZIP.swift +0 -130
- package/Sources/Private/Utility/Interpolatable/KeyframeGroup+Extensions.swift +0 -59
|
@@ -19,22 +19,35 @@ final class KeyframeGroup<T> {
|
|
|
19
19
|
|
|
20
20
|
// MARK: Lifecycle
|
|
21
21
|
|
|
22
|
-
init(
|
|
22
|
+
init(
|
|
23
|
+
keyframes: ContiguousArray<Keyframe<T>>,
|
|
24
|
+
unsupportedAfterEffectsExpression: String? = nil)
|
|
25
|
+
{
|
|
23
26
|
self.keyframes = keyframes
|
|
27
|
+
self.unsupportedAfterEffectsExpression = unsupportedAfterEffectsExpression
|
|
24
28
|
}
|
|
25
29
|
|
|
26
|
-
init(
|
|
30
|
+
init(
|
|
31
|
+
_ value: T,
|
|
32
|
+
unsupportedAfterEffectsExpression: String? = nil)
|
|
33
|
+
{
|
|
27
34
|
keyframes = [Keyframe(value)]
|
|
35
|
+
self.unsupportedAfterEffectsExpression = unsupportedAfterEffectsExpression
|
|
28
36
|
}
|
|
29
37
|
|
|
30
38
|
// MARK: Internal
|
|
31
39
|
|
|
32
40
|
enum KeyframeWrapperKey: String, CodingKey {
|
|
33
41
|
case keyframeData = "k"
|
|
42
|
+
case unsupportedAfterEffectsExpression = "x"
|
|
34
43
|
}
|
|
35
44
|
|
|
36
45
|
let keyframes: ContiguousArray<Keyframe<T>>
|
|
37
46
|
|
|
47
|
+
/// lottie-ios doesn't support After Effects expressions, but we parse them so we can log diagnostics.
|
|
48
|
+
/// More info: https://helpx.adobe.com/after-effects/using/expression-basics.html
|
|
49
|
+
let unsupportedAfterEffectsExpression: String?
|
|
50
|
+
|
|
38
51
|
}
|
|
39
52
|
|
|
40
53
|
// MARK: Decodable
|
|
@@ -42,10 +55,13 @@ final class KeyframeGroup<T> {
|
|
|
42
55
|
extension KeyframeGroup: Decodable where T: Decodable {
|
|
43
56
|
convenience init(from decoder: Decoder) throws {
|
|
44
57
|
let container = try decoder.container(keyedBy: KeyframeWrapperKey.self)
|
|
58
|
+
let unsupportedAfterEffectsExpression = try? container.decode(String.self, forKey: .unsupportedAfterEffectsExpression)
|
|
45
59
|
|
|
46
60
|
if let keyframeData: T = try? container.decode(T.self, forKey: .keyframeData) {
|
|
47
61
|
/// Try to decode raw value; No keyframe data.
|
|
48
|
-
self.init(
|
|
62
|
+
self.init(
|
|
63
|
+
keyframes: [Keyframe<T>(keyframeData)],
|
|
64
|
+
unsupportedAfterEffectsExpression: unsupportedAfterEffectsExpression)
|
|
49
65
|
} else {
|
|
50
66
|
// Decode and array of keyframes.
|
|
51
67
|
//
|
|
@@ -89,7 +105,9 @@ extension KeyframeGroup: Decodable where T: Decodable {
|
|
|
89
105
|
spatialOutTangent: keyframeData.spatialOutTangent))
|
|
90
106
|
previousKeyframeData = keyframeData
|
|
91
107
|
}
|
|
92
|
-
self.init(
|
|
108
|
+
self.init(
|
|
109
|
+
keyframes: keyframes,
|
|
110
|
+
unsupportedAfterEffectsExpression: unsupportedAfterEffectsExpression)
|
|
93
111
|
}
|
|
94
112
|
}
|
|
95
113
|
}
|
|
@@ -129,6 +147,7 @@ extension KeyframeGroup: Encodable where T: Encodable {
|
|
|
129
147
|
extension KeyframeGroup: DictionaryInitializable where T: AnyInitializable {
|
|
130
148
|
convenience init(dictionary: [String: Any]) throws {
|
|
131
149
|
var keyframes = ContiguousArray<Keyframe<T>>()
|
|
150
|
+
let unsupportedAfterEffectsExpression = dictionary[KeyframeWrapperKey.unsupportedAfterEffectsExpression.rawValue] as? String
|
|
132
151
|
if
|
|
133
152
|
let rawValue = dictionary[KeyframeWrapperKey.keyframeData.rawValue],
|
|
134
153
|
let value = try? T(value: rawValue)
|
|
@@ -162,7 +181,9 @@ extension KeyframeGroup: DictionaryInitializable where T: AnyInitializable {
|
|
|
162
181
|
}
|
|
163
182
|
}
|
|
164
183
|
|
|
165
|
-
self.init(
|
|
184
|
+
self.init(
|
|
185
|
+
keyframes: keyframes,
|
|
186
|
+
unsupportedAfterEffectsExpression: unsupportedAfterEffectsExpression)
|
|
166
187
|
}
|
|
167
188
|
}
|
|
168
189
|
|
|
@@ -199,9 +220,11 @@ extension Keyframe {
|
|
|
199
220
|
extension KeyframeGroup {
|
|
200
221
|
/// Maps the values of each individual keyframe in this group
|
|
201
222
|
func map<NewValue>(_ transformation: (T) throws -> NewValue) rethrows -> KeyframeGroup<NewValue> {
|
|
202
|
-
KeyframeGroup<NewValue>(
|
|
203
|
-
|
|
204
|
-
|
|
223
|
+
KeyframeGroup<NewValue>(
|
|
224
|
+
keyframes: ContiguousArray(try keyframes.map { keyframe in
|
|
225
|
+
keyframe.withValue(try transformation(keyframe.value))
|
|
226
|
+
}),
|
|
227
|
+
unsupportedAfterEffectsExpression: unsupportedAfterEffectsExpression)
|
|
205
228
|
}
|
|
206
229
|
}
|
|
207
230
|
|
|
@@ -55,14 +55,25 @@ final class Transform: Codable, DictionaryInitializable {
|
|
|
55
55
|
KeyframeGroup(LottieVector3D(x: Double(100), y: 100, z: 100))
|
|
56
56
|
|
|
57
57
|
// Rotation
|
|
58
|
-
if let
|
|
59
|
-
|
|
58
|
+
if let rotation = try container.decodeIfPresent(KeyframeGroup<LottieVector1D>.self, forKey: .rotationX) {
|
|
59
|
+
rotationX = rotation
|
|
60
60
|
} else {
|
|
61
|
-
|
|
62
|
-
|
|
61
|
+
rotationX = KeyframeGroup(LottieVector1D(0))
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
if let rotation = try container.decodeIfPresent(KeyframeGroup<LottieVector1D>.self, forKey: .rotationY) {
|
|
65
|
+
rotationY = rotation
|
|
66
|
+
} else {
|
|
67
|
+
rotationY = KeyframeGroup(LottieVector1D(0))
|
|
63
68
|
}
|
|
64
|
-
rotationZ = nil
|
|
65
69
|
|
|
70
|
+
if let rotation = try container.decodeIfPresent(KeyframeGroup<LottieVector1D>.self, forKey: .rotationZ) {
|
|
71
|
+
rotationZ = rotation
|
|
72
|
+
} else {
|
|
73
|
+
rotationZ = try container
|
|
74
|
+
.decodeIfPresent(KeyframeGroup<LottieVector1D>.self, forKey: .rotation) ?? KeyframeGroup(LottieVector1D(0))
|
|
75
|
+
}
|
|
76
|
+
rotation = nil
|
|
66
77
|
// Opacity
|
|
67
78
|
opacity = try container
|
|
68
79
|
.decodeIfPresent(KeyframeGroup<LottieVector1D>.self, forKey: .opacity) ?? KeyframeGroup(LottieVector1D(100))
|
|
@@ -114,20 +125,40 @@ final class Transform: Codable, DictionaryInitializable {
|
|
|
114
125
|
} else {
|
|
115
126
|
scale = KeyframeGroup(LottieVector3D(x: Double(100), y: 100, z: 100))
|
|
116
127
|
}
|
|
128
|
+
|
|
117
129
|
if
|
|
118
|
-
let rotationDictionary = dictionary[CodingKeys.
|
|
130
|
+
let rotationDictionary = dictionary[CodingKeys.rotationX.rawValue] as? [String: Any],
|
|
119
131
|
let rotation = try? KeyframeGroup<LottieVector1D>(dictionary: rotationDictionary)
|
|
120
132
|
{
|
|
121
|
-
|
|
122
|
-
} else
|
|
123
|
-
|
|
133
|
+
rotationX = rotation
|
|
134
|
+
} else {
|
|
135
|
+
rotationX = KeyframeGroup(LottieVector1D(0))
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
if
|
|
139
|
+
let rotationDictionary = dictionary[CodingKeys.rotationY.rawValue] as? [String: Any],
|
|
124
140
|
let rotation = try? KeyframeGroup<LottieVector1D>(dictionary: rotationDictionary)
|
|
125
141
|
{
|
|
126
|
-
|
|
142
|
+
rotationY = rotation
|
|
127
143
|
} else {
|
|
128
|
-
|
|
144
|
+
rotationY = KeyframeGroup(LottieVector1D(0))
|
|
129
145
|
}
|
|
130
|
-
|
|
146
|
+
|
|
147
|
+
if
|
|
148
|
+
let rotationDictionary = dictionary[CodingKeys.rotation.rawValue] as? [String: Any],
|
|
149
|
+
let rotation = try? KeyframeGroup<LottieVector1D>(dictionary: rotationDictionary)
|
|
150
|
+
{
|
|
151
|
+
rotationZ = rotation
|
|
152
|
+
} else if
|
|
153
|
+
let rotationDictionary = dictionary[CodingKeys.rotationZ.rawValue] as? [String: Any],
|
|
154
|
+
let rotation = try? KeyframeGroup<LottieVector1D>(dictionary: rotationDictionary)
|
|
155
|
+
{
|
|
156
|
+
rotationZ = rotation
|
|
157
|
+
}
|
|
158
|
+
else {
|
|
159
|
+
rotationZ = KeyframeGroup(LottieVector1D(0))
|
|
160
|
+
}
|
|
161
|
+
rotation = nil
|
|
131
162
|
if
|
|
132
163
|
let opacityDictionary = dictionary[CodingKeys.opacity.rawValue] as? [String: Any],
|
|
133
164
|
let opacity = try? KeyframeGroup<LottieVector1D>(dictionary: opacityDictionary)
|
|
@@ -147,6 +178,8 @@ final class Transform: Codable, DictionaryInitializable {
|
|
|
147
178
|
case positionY = "py"
|
|
148
179
|
case scale = "s"
|
|
149
180
|
case rotation = "r"
|
|
181
|
+
case rotationX = "rx"
|
|
182
|
+
case rotationY = "ry"
|
|
150
183
|
case rotationZ = "rz"
|
|
151
184
|
case opacity = "o"
|
|
152
185
|
}
|
|
@@ -169,15 +202,23 @@ final class Transform: Codable, DictionaryInitializable {
|
|
|
169
202
|
/// The positionY of the transform. This is nil if the position property is set.
|
|
170
203
|
let positionY: KeyframeGroup<LottieVector1D>?
|
|
171
204
|
|
|
172
|
-
/// The scale of the transform
|
|
205
|
+
/// The scale of the transform.
|
|
173
206
|
let scale: KeyframeGroup<LottieVector3D>
|
|
174
207
|
|
|
175
|
-
/// The rotation of the transform
|
|
176
|
-
let
|
|
208
|
+
/// The rotation of the transform on X axis.
|
|
209
|
+
let rotationX: KeyframeGroup<LottieVector1D>
|
|
210
|
+
|
|
211
|
+
/// The rotation of the transform on Y axis.
|
|
212
|
+
let rotationY: KeyframeGroup<LottieVector1D>
|
|
213
|
+
|
|
214
|
+
/// The rotation of the transform on Z axis.
|
|
215
|
+
let rotationZ: KeyframeGroup<LottieVector1D>
|
|
177
216
|
|
|
178
217
|
/// The opacity of the transform.
|
|
179
218
|
let opacity: KeyframeGroup<LottieVector1D>
|
|
180
219
|
|
|
181
|
-
|
|
182
|
-
|
|
220
|
+
// MARK: Private
|
|
221
|
+
|
|
222
|
+
/// Here for the CodingKeys.rotation = "r". `r` and `rz` are the same.
|
|
223
|
+
private let rotation: KeyframeGroup<LottieVector1D>?
|
|
183
224
|
}
|
|
@@ -22,8 +22,19 @@ final class Repeater: ShapeItem {
|
|
|
22
22
|
.decodeIfPresent(KeyframeGroup<LottieVector1D>.self, forKey: .startOpacity) ?? KeyframeGroup(LottieVector1D(100))
|
|
23
23
|
endOpacity = try transformContainer
|
|
24
24
|
.decodeIfPresent(KeyframeGroup<LottieVector1D>.self, forKey: .endOpacity) ?? KeyframeGroup(LottieVector1D(100))
|
|
25
|
-
rotation = try transformContainer
|
|
26
|
-
|
|
25
|
+
if let rotation = try transformContainer.decodeIfPresent(KeyframeGroup<LottieVector1D>.self, forKey: .rotation) {
|
|
26
|
+
rotationZ = rotation
|
|
27
|
+
} else if let rotation = try transformContainer.decodeIfPresent(KeyframeGroup<LottieVector1D>.self, forKey: .rotationZ) {
|
|
28
|
+
rotationZ = rotation
|
|
29
|
+
} else {
|
|
30
|
+
rotationZ = KeyframeGroup(LottieVector1D(0))
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
rotationX = try transformContainer
|
|
34
|
+
.decodeIfPresent(KeyframeGroup<LottieVector1D>.self, forKey: .rotationX) ?? KeyframeGroup(LottieVector1D(0))
|
|
35
|
+
rotationY = try transformContainer
|
|
36
|
+
.decodeIfPresent(KeyframeGroup<LottieVector1D>.self, forKey: .rotationY) ?? KeyframeGroup(LottieVector1D(0))
|
|
37
|
+
|
|
27
38
|
position = try transformContainer
|
|
28
39
|
.decodeIfPresent(KeyframeGroup<LottieVector3D>.self, forKey: .position) ??
|
|
29
40
|
KeyframeGroup(LottieVector3D(x: Double(0), y: 0, z: 0))
|
|
@@ -58,10 +69,22 @@ final class Repeater: ShapeItem {
|
|
|
58
69
|
} else {
|
|
59
70
|
endOpacity = KeyframeGroup(LottieVector1D(100))
|
|
60
71
|
}
|
|
72
|
+
if let rotationDictionary = transformDictionary[TransformKeys.rotationX.rawValue] as? [String: Any] {
|
|
73
|
+
rotationX = try KeyframeGroup<LottieVector1D>(dictionary: rotationDictionary)
|
|
74
|
+
} else {
|
|
75
|
+
rotationX = KeyframeGroup(LottieVector1D(0))
|
|
76
|
+
}
|
|
77
|
+
if let rotationDictionary = transformDictionary[TransformKeys.rotationY.rawValue] as? [String: Any] {
|
|
78
|
+
rotationY = try KeyframeGroup<LottieVector1D>(dictionary: rotationDictionary)
|
|
79
|
+
} else {
|
|
80
|
+
rotationY = KeyframeGroup(LottieVector1D(0))
|
|
81
|
+
}
|
|
61
82
|
if let rotationDictionary = transformDictionary[TransformKeys.rotation.rawValue] as? [String: Any] {
|
|
62
|
-
|
|
83
|
+
rotationZ = try KeyframeGroup<LottieVector1D>(dictionary: rotationDictionary)
|
|
84
|
+
} else if let rotationDictionary = transformDictionary[TransformKeys.rotationZ.rawValue] as? [String: Any] {
|
|
85
|
+
rotationZ = try KeyframeGroup<LottieVector1D>(dictionary: rotationDictionary)
|
|
63
86
|
} else {
|
|
64
|
-
|
|
87
|
+
rotationZ = KeyframeGroup(LottieVector1D(0))
|
|
65
88
|
}
|
|
66
89
|
if let positionDictionary = transformDictionary[TransformKeys.position.rawValue] as? [String: Any] {
|
|
67
90
|
position = try KeyframeGroup<LottieVector3D>(dictionary: positionDictionary)
|
|
@@ -95,8 +118,14 @@ final class Repeater: ShapeItem {
|
|
|
95
118
|
/// End opacity
|
|
96
119
|
let endOpacity: KeyframeGroup<LottieVector1D>
|
|
97
120
|
|
|
98
|
-
/// The rotation
|
|
99
|
-
let
|
|
121
|
+
/// The rotation on X axis
|
|
122
|
+
let rotationX: KeyframeGroup<LottieVector1D>
|
|
123
|
+
|
|
124
|
+
/// The rotation on Y axis
|
|
125
|
+
let rotationY: KeyframeGroup<LottieVector1D>
|
|
126
|
+
|
|
127
|
+
/// The rotation on Z axis
|
|
128
|
+
let rotationZ: KeyframeGroup<LottieVector1D>
|
|
100
129
|
|
|
101
130
|
/// Anchor Point
|
|
102
131
|
let anchorPoint: KeyframeGroup<LottieVector3D>
|
|
@@ -115,7 +144,9 @@ final class Repeater: ShapeItem {
|
|
|
115
144
|
var transformContainer = container.nestedContainer(keyedBy: TransformKeys.self, forKey: .transform)
|
|
116
145
|
try transformContainer.encode(startOpacity, forKey: .startOpacity)
|
|
117
146
|
try transformContainer.encode(endOpacity, forKey: .endOpacity)
|
|
118
|
-
try transformContainer.encode(
|
|
147
|
+
try transformContainer.encode(rotationX, forKey: .rotationX)
|
|
148
|
+
try transformContainer.encode(rotationY, forKey: .rotationY)
|
|
149
|
+
try transformContainer.encode(rotationZ, forKey: .rotationZ)
|
|
119
150
|
try transformContainer.encode(position, forKey: .position)
|
|
120
151
|
try transformContainer.encode(anchorPoint, forKey: .anchorPoint)
|
|
121
152
|
try transformContainer.encode(scale, forKey: .scale)
|
|
@@ -131,6 +162,9 @@ final class Repeater: ShapeItem {
|
|
|
131
162
|
|
|
132
163
|
private enum TransformKeys: String, CodingKey {
|
|
133
164
|
case rotation = "r"
|
|
165
|
+
case rotationX = "rx"
|
|
166
|
+
case rotationY = "ry"
|
|
167
|
+
case rotationZ = "rz"
|
|
134
168
|
case startOpacity = "so"
|
|
135
169
|
case endOpacity = "eo"
|
|
136
170
|
case anchorPoint = "a"
|
|
@@ -22,8 +22,25 @@ final class ShapeTransform: ShapeItem {
|
|
|
22
22
|
scale = try container
|
|
23
23
|
.decodeIfPresent(KeyframeGroup<LottieVector3D>.self, forKey: .scale) ??
|
|
24
24
|
KeyframeGroup(LottieVector3D(x: Double(100), y: 100, z: 100))
|
|
25
|
-
|
|
26
|
-
|
|
25
|
+
|
|
26
|
+
rotationX = try container
|
|
27
|
+
.decodeIfPresent(KeyframeGroup<LottieVector1D>.self, forKey: .rotationX) ?? KeyframeGroup(LottieVector1D(0))
|
|
28
|
+
rotationY = try container
|
|
29
|
+
.decodeIfPresent(KeyframeGroup<LottieVector1D>.self, forKey: .rotationY) ?? KeyframeGroup(LottieVector1D(0))
|
|
30
|
+
if
|
|
31
|
+
let rotation = try container
|
|
32
|
+
.decodeIfPresent(KeyframeGroup<LottieVector1D>.self, forKey: .rotation)
|
|
33
|
+
{
|
|
34
|
+
rotationZ = rotation
|
|
35
|
+
} else if
|
|
36
|
+
let rotation = try container
|
|
37
|
+
.decodeIfPresent(KeyframeGroup<LottieVector1D>.self, forKey: .rotationZ)
|
|
38
|
+
{
|
|
39
|
+
rotationZ = rotation
|
|
40
|
+
} else {
|
|
41
|
+
rotationZ = KeyframeGroup(LottieVector1D(0))
|
|
42
|
+
}
|
|
43
|
+
|
|
27
44
|
opacity = try container
|
|
28
45
|
.decodeIfPresent(KeyframeGroup<LottieVector1D>.self, forKey: .opacity) ?? KeyframeGroup(LottieVector1D(100))
|
|
29
46
|
skew = try container.decodeIfPresent(KeyframeGroup<LottieVector1D>.self, forKey: .skew) ?? KeyframeGroup(LottieVector1D(0))
|
|
@@ -57,14 +74,39 @@ final class ShapeTransform: ShapeItem {
|
|
|
57
74
|
} else {
|
|
58
75
|
scale = KeyframeGroup(LottieVector3D(x: Double(100), y: 100, z: 100))
|
|
59
76
|
}
|
|
77
|
+
|
|
78
|
+
if
|
|
79
|
+
let rotationDictionary = dictionary[CodingKeys.rotationX.rawValue] as? [String: Any],
|
|
80
|
+
let rotation = try? KeyframeGroup<LottieVector1D>(dictionary: rotationDictionary)
|
|
81
|
+
{
|
|
82
|
+
rotationX = rotation
|
|
83
|
+
} else {
|
|
84
|
+
rotationX = KeyframeGroup(LottieVector1D(0))
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
if
|
|
88
|
+
let rotationDictionary = dictionary[CodingKeys.rotationY.rawValue] as? [String: Any],
|
|
89
|
+
let rotation = try? KeyframeGroup<LottieVector1D>(dictionary: rotationDictionary)
|
|
90
|
+
{
|
|
91
|
+
rotationY = rotation
|
|
92
|
+
} else {
|
|
93
|
+
rotationY = KeyframeGroup(LottieVector1D(0))
|
|
94
|
+
}
|
|
95
|
+
|
|
60
96
|
if
|
|
61
97
|
let rotationDictionary = dictionary[CodingKeys.rotation.rawValue] as? [String: Any],
|
|
62
98
|
let rotation = try? KeyframeGroup<LottieVector1D>(dictionary: rotationDictionary)
|
|
63
99
|
{
|
|
64
|
-
|
|
100
|
+
rotationZ = rotation
|
|
101
|
+
} else if
|
|
102
|
+
let rotationDictionary = dictionary[CodingKeys.rotationZ.rawValue] as? [String: Any],
|
|
103
|
+
let rotation = try? KeyframeGroup<LottieVector1D>(dictionary: rotationDictionary)
|
|
104
|
+
{
|
|
105
|
+
rotationZ = rotation
|
|
65
106
|
} else {
|
|
66
|
-
|
|
107
|
+
rotationZ = KeyframeGroup(LottieVector1D(0))
|
|
67
108
|
}
|
|
109
|
+
|
|
68
110
|
if
|
|
69
111
|
let opacityDictionary = dictionary[CodingKeys.opacity.rawValue] as? [String: Any],
|
|
70
112
|
let opacity = try? KeyframeGroup<LottieVector1D>(dictionary: opacityDictionary)
|
|
@@ -89,6 +131,7 @@ final class ShapeTransform: ShapeItem {
|
|
|
89
131
|
} else {
|
|
90
132
|
skewAxis = KeyframeGroup(LottieVector1D(0))
|
|
91
133
|
}
|
|
134
|
+
|
|
92
135
|
try super.init(dictionary: dictionary)
|
|
93
136
|
}
|
|
94
137
|
|
|
@@ -103,8 +146,14 @@ final class ShapeTransform: ShapeItem {
|
|
|
103
146
|
/// Scale
|
|
104
147
|
let scale: KeyframeGroup<LottieVector3D>
|
|
105
148
|
|
|
106
|
-
/// Rotation
|
|
107
|
-
let
|
|
149
|
+
/// Rotation on X axis
|
|
150
|
+
let rotationX: KeyframeGroup<LottieVector1D>
|
|
151
|
+
|
|
152
|
+
/// Rotation on Y axis
|
|
153
|
+
let rotationY: KeyframeGroup<LottieVector1D>
|
|
154
|
+
|
|
155
|
+
/// Rotation on Z axis
|
|
156
|
+
let rotationZ: KeyframeGroup<LottieVector1D>
|
|
108
157
|
|
|
109
158
|
/// opacity
|
|
110
159
|
let opacity: KeyframeGroup<LottieVector1D>
|
|
@@ -130,7 +179,9 @@ final class ShapeTransform: ShapeItem {
|
|
|
130
179
|
try container.encode(anchor, forKey: .anchor)
|
|
131
180
|
try container.encode(position, forKey: .position)
|
|
132
181
|
try container.encode(scale, forKey: .scale)
|
|
133
|
-
try container.encode(
|
|
182
|
+
try container.encode(rotationX, forKey: .rotationX)
|
|
183
|
+
try container.encode(rotationY, forKey: .rotationY)
|
|
184
|
+
try container.encode(rotationZ, forKey: .rotationZ)
|
|
134
185
|
try container.encode(opacity, forKey: .opacity)
|
|
135
186
|
try container.encode(skew, forKey: .skew)
|
|
136
187
|
try container.encode(skewAxis, forKey: .skewAxis)
|
|
@@ -143,6 +194,9 @@ final class ShapeTransform: ShapeItem {
|
|
|
143
194
|
case position = "p"
|
|
144
195
|
case scale = "s"
|
|
145
196
|
case rotation = "r"
|
|
197
|
+
case rotationX = "rx"
|
|
198
|
+
case rotationY = "ry"
|
|
199
|
+
case rotationZ = "rz"
|
|
146
200
|
case opacity = "o"
|
|
147
201
|
case skew = "sk"
|
|
148
202
|
case skewAxis = "sa"
|
|
@@ -24,7 +24,15 @@ final class TextAnimator: Codable, DictionaryInitializable {
|
|
|
24
24
|
scale = try animatorContainer.decodeIfPresent(KeyframeGroup<LottieVector3D>.self, forKey: .scale)
|
|
25
25
|
skew = try animatorContainer.decodeIfPresent(KeyframeGroup<LottieVector1D>.self, forKey: .skew)
|
|
26
26
|
skewAxis = try animatorContainer.decodeIfPresent(KeyframeGroup<LottieVector1D>.self, forKey: .skewAxis)
|
|
27
|
-
|
|
27
|
+
rotationX = try animatorContainer.decodeIfPresent(KeyframeGroup<LottieVector1D>.self, forKey: .rotationX)
|
|
28
|
+
rotationY = try animatorContainer.decodeIfPresent(KeyframeGroup<LottieVector1D>.self, forKey: .rotationY)
|
|
29
|
+
if let rotation = try animatorContainer.decodeIfPresent(KeyframeGroup<LottieVector1D>.self, forKey: .rotation) {
|
|
30
|
+
rotationZ = rotation
|
|
31
|
+
} else if let rotation = try animatorContainer.decodeIfPresent(KeyframeGroup<LottieVector1D>.self, forKey: .rotationZ) {
|
|
32
|
+
rotationZ = rotation
|
|
33
|
+
} else {
|
|
34
|
+
rotationZ = nil
|
|
35
|
+
}
|
|
28
36
|
opacity = try animatorContainer.decodeIfPresent(KeyframeGroup<LottieVector1D>.self, forKey: .opacity)
|
|
29
37
|
}
|
|
30
38
|
|
|
@@ -76,11 +84,26 @@ final class TextAnimator: Codable, DictionaryInitializable {
|
|
|
76
84
|
} else {
|
|
77
85
|
skewAxis = nil
|
|
78
86
|
}
|
|
87
|
+
if let rotationDictionary = animatorDictionary[TextAnimatorKeys.rotationX.rawValue] as? [String: Any] {
|
|
88
|
+
rotationX = try? KeyframeGroup<LottieVector1D>(dictionary: rotationDictionary)
|
|
89
|
+
} else {
|
|
90
|
+
rotationX = nil
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
if let rotationDictionary = animatorDictionary[TextAnimatorKeys.rotationY.rawValue] as? [String: Any] {
|
|
94
|
+
rotationY = try? KeyframeGroup<LottieVector1D>(dictionary: rotationDictionary)
|
|
95
|
+
} else {
|
|
96
|
+
rotationY = nil
|
|
97
|
+
}
|
|
98
|
+
|
|
79
99
|
if let rotationDictionary = animatorDictionary[TextAnimatorKeys.rotation.rawValue] as? [String: Any] {
|
|
80
|
-
|
|
100
|
+
rotationZ = try? KeyframeGroup<LottieVector1D>(dictionary: rotationDictionary)
|
|
101
|
+
} else if let rotationDictionary = animatorDictionary[TextAnimatorKeys.rotationZ.rawValue] as? [String: Any] {
|
|
102
|
+
rotationZ = try? KeyframeGroup<LottieVector1D>(dictionary: rotationDictionary)
|
|
81
103
|
} else {
|
|
82
|
-
|
|
104
|
+
rotationZ = nil
|
|
83
105
|
}
|
|
106
|
+
|
|
84
107
|
if let opacityDictionary = animatorDictionary[TextAnimatorKeys.opacity.rawValue] as? [String: Any] {
|
|
85
108
|
opacity = try KeyframeGroup<LottieVector1D>(dictionary: opacityDictionary)
|
|
86
109
|
} else {
|
|
@@ -107,8 +130,14 @@ final class TextAnimator: Codable, DictionaryInitializable {
|
|
|
107
130
|
/// Skew Axis
|
|
108
131
|
let skewAxis: KeyframeGroup<LottieVector1D>?
|
|
109
132
|
|
|
110
|
-
/// Rotation
|
|
111
|
-
let
|
|
133
|
+
/// Rotation on X axis
|
|
134
|
+
let rotationX: KeyframeGroup<LottieVector1D>?
|
|
135
|
+
|
|
136
|
+
/// Rotation on Y axis
|
|
137
|
+
let rotationY: KeyframeGroup<LottieVector1D>?
|
|
138
|
+
|
|
139
|
+
/// Rotation on Z axis
|
|
140
|
+
let rotationZ: KeyframeGroup<LottieVector1D>?
|
|
112
141
|
|
|
113
142
|
/// Opacity
|
|
114
143
|
let opacity: KeyframeGroup<LottieVector1D>?
|
|
@@ -159,6 +188,9 @@ final class TextAnimator: Codable, DictionaryInitializable {
|
|
|
159
188
|
case skew = "sk"
|
|
160
189
|
case skewAxis = "sa"
|
|
161
190
|
case rotation = "r"
|
|
191
|
+
case rotationX = "rx"
|
|
192
|
+
case rotationY = "ry"
|
|
193
|
+
case rotationZ = "rz"
|
|
162
194
|
case opacity = "o"
|
|
163
195
|
}
|
|
164
196
|
}
|
|
@@ -7,6 +7,8 @@ import QuartzCore
|
|
|
7
7
|
|
|
8
8
|
/// A root `CALayer` responsible for playing a Lottie animation
|
|
9
9
|
protocol RootAnimationLayer: CALayer {
|
|
10
|
+
var animationView: LottieAnimationView? { get set }
|
|
11
|
+
|
|
10
12
|
var currentFrame: AnimationFrameTime { get set }
|
|
11
13
|
var renderScale: CGFloat { get set }
|
|
12
14
|
var respectAnimationFrameRate: Bool { get set }
|
|
@@ -32,7 +34,7 @@ protocol RootAnimationLayer: CALayer {
|
|
|
32
34
|
func reloadImages()
|
|
33
35
|
func forceDisplayUpdate()
|
|
34
36
|
func logHierarchyKeypaths()
|
|
35
|
-
|
|
37
|
+
func allHierarchyKeypaths() -> [String]
|
|
36
38
|
func setValueProvider(_ valueProvider: AnyValueProvider, keypath: AnimationKeypath)
|
|
37
39
|
func getValue(for keypath: AnimationKeypath, atFrame: AnimationFrameTime?) -> Any?
|
|
38
40
|
func getOriginalValue(for keypath: AnimationKeypath, atFrame: AnimationFrameTime?) -> Any?
|
|
@@ -115,20 +115,28 @@ extension KeypathSearchable {
|
|
|
115
115
|
return nil
|
|
116
116
|
}
|
|
117
117
|
|
|
118
|
-
|
|
118
|
+
/// Computes the list of animation keypaths that descend from this layer
|
|
119
|
+
func allKeypaths(for keyPath: AnimationKeypath? = nil) -> [String] {
|
|
120
|
+
var allKeypaths: [String] = []
|
|
121
|
+
|
|
119
122
|
let newKeypath: AnimationKeypath
|
|
120
123
|
if let previousKeypath = keyPath {
|
|
121
124
|
newKeypath = previousKeypath.appendingKey(keypathName)
|
|
122
125
|
} else {
|
|
123
126
|
newKeypath = AnimationKeypath(keys: [keypathName])
|
|
124
127
|
}
|
|
125
|
-
|
|
128
|
+
|
|
129
|
+
allKeypaths.append(newKeypath.fullPath)
|
|
130
|
+
|
|
126
131
|
for key in keypathProperties.keys {
|
|
127
|
-
|
|
132
|
+
allKeypaths.append(newKeypath.appendingKey(key).fullPath)
|
|
128
133
|
}
|
|
134
|
+
|
|
129
135
|
for child in childKeypaths {
|
|
130
|
-
child.
|
|
136
|
+
allKeypaths.append(contentsOf: child.allKeypaths(for: newKeypath))
|
|
131
137
|
}
|
|
138
|
+
|
|
139
|
+
return allKeypaths
|
|
132
140
|
}
|
|
133
141
|
}
|
|
134
142
|
|
|
@@ -14,14 +14,24 @@ import AppKit
|
|
|
14
14
|
|
|
15
15
|
extension Data {
|
|
16
16
|
|
|
17
|
-
|
|
17
|
+
init(assetName: String, in bundle: Bundle) throws {
|
|
18
18
|
#if canImport(UIKit)
|
|
19
|
-
|
|
19
|
+
if let asset = NSDataAsset(name: assetName, bundle: bundle) {
|
|
20
|
+
self = asset.data
|
|
21
|
+
return
|
|
22
|
+
} else {
|
|
23
|
+
throw DotLottieError.assetNotFound(name: assetName, bundle: bundle)
|
|
24
|
+
}
|
|
20
25
|
#else
|
|
21
26
|
if #available(macOS 10.11, *) {
|
|
22
|
-
|
|
27
|
+
if let asset = NSDataAsset(name: assetName, bundle: bundle) {
|
|
28
|
+
self = asset.data
|
|
29
|
+
return
|
|
30
|
+
} else {
|
|
31
|
+
throw DotLottieError.assetNotFound(name: assetName, bundle: bundle)
|
|
32
|
+
}
|
|
23
33
|
}
|
|
24
|
-
|
|
34
|
+
throw DotLottieError.loadingFromAssetNotSupported
|
|
25
35
|
#endif
|
|
26
36
|
}
|
|
27
37
|
}
|
|
@@ -83,6 +83,8 @@ extension BezierPath {
|
|
|
83
83
|
uniquePath.addVertex(elements[i].vertex)
|
|
84
84
|
}
|
|
85
85
|
|
|
86
|
+
var pathHasRoundedCorner = false
|
|
87
|
+
|
|
86
88
|
for elementIndex in 0..<uniquePath.elements.count {
|
|
87
89
|
currentVertex = uniquePath.elements[elementIndex].vertex
|
|
88
90
|
|
|
@@ -96,6 +98,9 @@ extension BezierPath {
|
|
|
96
98
|
continue
|
|
97
99
|
}
|
|
98
100
|
|
|
101
|
+
// Track whether or not this path has at least one rounded corner
|
|
102
|
+
pathHasRoundedCorner = true
|
|
103
|
+
|
|
99
104
|
// Do not round start and end if not closed
|
|
100
105
|
if !newPath.closed, elementIndex == 0 || elementIndex == uniquePath.elements.count - 1 {
|
|
101
106
|
newPath.addVertex(currentVertex)
|
|
@@ -142,6 +147,12 @@ extension BezierPath {
|
|
|
142
147
|
}
|
|
143
148
|
}
|
|
144
149
|
|
|
150
|
+
// If we didn't need to apply the corner radius to any of the corners,
|
|
151
|
+
// just use the original given path instead of modifying it.
|
|
152
|
+
if !pathHasRoundedCorner {
|
|
153
|
+
return self
|
|
154
|
+
}
|
|
155
|
+
|
|
145
156
|
return newPath
|
|
146
157
|
}
|
|
147
158
|
}
|
|
@@ -38,22 +38,18 @@ extension LottieColor: Codable {
|
|
|
38
38
|
b1 = 0
|
|
39
39
|
}
|
|
40
40
|
|
|
41
|
-
|
|
42
|
-
if !container.isAtEnd {
|
|
43
|
-
a1 = try container.decode(Double.self)
|
|
44
|
-
} else {
|
|
45
|
-
a1 = 1
|
|
46
|
-
}
|
|
47
|
-
if r1 > 1, g1 > 1, b1 > 1, a1 > 1 {
|
|
41
|
+
if r1 > 1, g1 > 1, b1 > 1 {
|
|
48
42
|
r1 = r1 / 255
|
|
49
43
|
g1 = g1 / 255
|
|
50
44
|
b1 = b1 / 255
|
|
51
|
-
a1 = a1 / 255
|
|
52
45
|
}
|
|
53
46
|
r = r1
|
|
54
47
|
g = g1
|
|
55
48
|
b = b1
|
|
56
|
-
|
|
49
|
+
|
|
50
|
+
// The Lottie JSON schema supports alpha values in theory, as the fourth value in this array.
|
|
51
|
+
// We intentionally do not support this, though, for consistency with Lottie on other platforms.
|
|
52
|
+
a = 1
|
|
57
53
|
}
|
|
58
54
|
|
|
59
55
|
// MARK: Public
|
|
@@ -79,17 +75,18 @@ extension LottieColor: AnyInitializable {
|
|
|
79
75
|
var r: Double = array.count > 0 ? array.removeFirst() : 0
|
|
80
76
|
var g: Double = array.count > 0 ? array.removeFirst() : 0
|
|
81
77
|
var b: Double = array.count > 0 ? array.removeFirst() : 0
|
|
82
|
-
|
|
83
|
-
if r > 1, g > 1, b > 1, a > 1 {
|
|
78
|
+
if r > 1, g > 1, b > 1 {
|
|
84
79
|
r /= 255
|
|
85
80
|
g /= 255
|
|
86
81
|
b /= 255
|
|
87
|
-
a /= 255
|
|
88
82
|
}
|
|
89
83
|
self.r = r
|
|
90
84
|
self.g = g
|
|
91
85
|
self.b = b
|
|
92
|
-
|
|
86
|
+
|
|
87
|
+
// The Lottie JSON schema supports alpha values in theory, as the fourth value in this array.
|
|
88
|
+
// We intentionally do not support this, though, for consistency with Lottie on other platforms.
|
|
89
|
+
a = 1
|
|
93
90
|
}
|
|
94
91
|
|
|
95
92
|
}
|