lottie-ios 4.4.2 → 4.5.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 +12 -39
- package/Lottie.xcodeproj/project.pbxproj +28 -0
- package/Lottie.xcworkspace/xcuserdata/calstephens.xcuserdatad/UserInterfaceState.xcuserstate +0 -0
- package/Lottie.xcworkspace/xcuserdata/calstephens.xcuserdatad/xcdebugger/Breakpoints_v2.xcbkptlist +19 -6
- package/Package.resolved +2 -2
- package/Package.swift +1 -1
- package/README.md +2 -2
- package/Rakefile +1 -1
- package/Sources/PrivacyInfo.xcprivacy +1 -1
- package/Sources/Private/CoreAnimation/Animations/CALayer+addAnimation.swift +2 -2
- package/Sources/Private/CoreAnimation/Animations/TransformAnimations.swift +6 -6
- package/Sources/Private/CoreAnimation/CoreAnimationLayer.swift +24 -9
- package/Sources/Private/CoreAnimation/Extensions/Keyframes+combined.swift +2 -2
- package/Sources/Private/CoreAnimation/Layers/AnimationLayer.swift +7 -7
- package/Sources/Private/CoreAnimation/Layers/ShapeItemLayer.swift +12 -12
- package/Sources/Private/CoreAnimation/Layers/ShapeLayer.swift +19 -19
- package/Sources/Private/CoreAnimation/ValueProviderStore.swift +4 -4
- package/Sources/Private/EmbeddedLibraries/EpoxyCore/SwiftUI/LayoutUtilities/SwiftUIMeasurementContainer.swift +13 -6
- package/Sources/Private/EmbeddedLibraries/LRUCache/LRUCache.swift +3 -3
- package/Sources/Private/EmbeddedLibraries/ZipFoundation/Archive+BackingConfiguration.swift +10 -6
- package/Sources/Private/EmbeddedLibraries/ZipFoundation/Archive+Helpers.swift +4 -0
- package/Sources/Private/EmbeddedLibraries/ZipFoundation/Archive+Progress.swift +2 -2
- package/Sources/Private/EmbeddedLibraries/ZipFoundation/Archive+Reading.swift +5 -0
- package/Sources/Private/EmbeddedLibraries/ZipFoundation/Archive+Writing.swift +2 -0
- package/Sources/Private/EmbeddedLibraries/ZipFoundation/Data+Compression.swift +1 -0
- package/Sources/Private/EmbeddedLibraries/ZipFoundation/Entry+ZIP64.swift +2 -2
- package/Sources/Private/EmbeddedLibraries/ZipFoundation/Entry.swift +1 -0
- package/Sources/Private/EmbeddedLibraries/ZipFoundation/FileManager+ZIP.swift +10 -9
- package/Sources/Private/MainThread/LayerContainers/CompLayers/CompositionLayer.swift +19 -0
- package/Sources/Private/MainThread/LayerContainers/CompLayers/MaskContainerLayer.swift +7 -7
- package/Sources/Private/MainThread/LayerContainers/CompLayers/PreCompositionLayer.swift +3 -0
- package/Sources/Private/MainThread/LayerContainers/CompLayers/TextCompositionLayer.swift +24 -14
- package/Sources/Private/MainThread/LayerContainers/MainThreadAnimationLayer.swift +11 -10
- package/Sources/Private/MainThread/LayerContainers/Utility/CompositionLayersInitializer.swift +2 -0
- package/Sources/Private/MainThread/LayerContainers/Utility/CoreTextRenderLayer.swift +102 -17
- package/Sources/Private/MainThread/LayerContainers/Utility/LayerTransformNode.swift +11 -11
- package/Sources/Private/MainThread/NodeRenderSystem/Extensions/ItemsExtension.swift +2 -0
- package/Sources/Private/MainThread/NodeRenderSystem/Nodes/LayerEffectNodes/DropShadowNode.swift +102 -0
- package/Sources/Private/MainThread/NodeRenderSystem/Nodes/LayerEffectNodes/LayerEffectNode.swift +24 -0
- package/Sources/Private/MainThread/NodeRenderSystem/Nodes/OutputNodes/Renderables/FillRenderer.swift +4 -4
- package/Sources/Private/MainThread/NodeRenderSystem/Nodes/OutputNodes/Renderables/StrokeRenderer.swift +16 -16
- package/Sources/Private/MainThread/NodeRenderSystem/Nodes/PathNodes/PolygonNode.swift +4 -4
- package/Sources/Private/MainThread/NodeRenderSystem/Nodes/RenderNodes/StrokeNode.swift +5 -5
- package/Sources/Private/MainThread/NodeRenderSystem/Nodes/Text/TextAnimatorNode.swift +60 -1
- package/Sources/Private/Model/DotLottie/DotLottieAnimation.swift +2 -2
- package/Sources/Private/Model/Keyframes/KeyframeGroup.swift +9 -6
- package/Sources/Private/Model/LayerEffects/EffectValues/EffectValue.swift +4 -4
- package/Sources/Private/Model/LayerEffects/LayerEffect.swift +2 -2
- package/Sources/Private/Model/LayerStyles/LayerStyle.swift +2 -2
- package/Sources/Private/Model/Layers/LayerModel.swift +7 -7
- package/Sources/Private/Model/ShapeItems/ShapeItem.swift +15 -15
- package/Sources/Private/Model/Text/TextAnimator.swift +47 -1
- package/Sources/Private/Utility/Debugging/LayerDebugging.swift +6 -6
- package/Sources/Private/Utility/Extensions/AnimationKeypathExtension.swift +16 -16
- package/Sources/Private/Utility/Extensions/BlendMode+Filter.swift +16 -16
- package/Sources/Private/Utility/Extensions/CGColor+RGB.swift +18 -0
- package/Sources/Private/Utility/Extensions/CGFloatExtensions.swift +23 -23
- package/Sources/Private/Utility/Extensions/MathKit.swift +4 -11
- package/Sources/Private/Utility/Interpolatable/InterpolatableExtensions.swift +9 -3
- package/Sources/Private/Utility/LottieAnimationSource.swift +4 -4
- package/Sources/Private/Utility/Primitives/BezierPath.swift +16 -2
- package/Sources/Private/Utility/Primitives/BezierPathRoundExtension.swift +2 -2
- package/Sources/Private/Utility/Primitives/ColorExtension.swift +20 -20
- package/Sources/Private/Utility/Primitives/CompoundBezierPath.swift +9 -9
- package/Sources/Public/Animation/LottieAnimationLayer.swift +36 -31
- package/Sources/Public/Animation/LottieAnimationView.swift +15 -15
- package/Sources/Public/Animation/LottieAnimationViewInitializers.swift +4 -0
- package/Sources/Public/Animation/LottiePlaybackMode.swift +12 -12
- package/Sources/Public/Animation/LottieView.swift +53 -5
- package/Sources/Public/Configuration/ReducedMotionOption.swift +5 -5
- package/Sources/Public/Configuration/RenderingEngineOption.swift +4 -4
- package/Sources/Public/Controls/AnimatedSwitch.swift +2 -2
- package/Sources/Public/DotLottie/DotLottieFile.swift +2 -2
- package/Sources/Public/DynamicProperties/AnyValueProvider.swift +9 -9
- package/Sources/Public/DynamicProperties/ValueProviders/GradientValueProvider.swift +2 -2
- package/Sources/Public/Keyframes/Interpolatable.swift +2 -2
- package/Sources/Public/Primitives/LottieColor.swift +3 -3
- package/Sources/Public/TextProvider/AnimationTextProvider.swift +4 -4
- package/Sources/Public/iOS/Compatibility/CompatibleAnimationView.swift +13 -12
- package/Version.xcconfig +2 -2
- package/lottie-ios.podspec +2 -2
- package/package.json +1 -1
package/Sources/Private/MainThread/NodeRenderSystem/Nodes/LayerEffectNodes/DropShadowNode.swift
ADDED
|
@@ -0,0 +1,102 @@
|
|
|
1
|
+
// Created by Lan Xu on 2024/6/7.
|
|
2
|
+
// Copyright © 2024 Airbnb Inc. All rights reserved.
|
|
3
|
+
|
|
4
|
+
import QuartzCore
|
|
5
|
+
|
|
6
|
+
// MARK: - DropShadowNode
|
|
7
|
+
|
|
8
|
+
final class DropShadowNode: LayerEffectNode {
|
|
9
|
+
|
|
10
|
+
// MARK: Lifecycle
|
|
11
|
+
|
|
12
|
+
init(model: DropShadowModel) {
|
|
13
|
+
properties = DropShadowNodeProperties(model: model)
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
// MARK: Internal
|
|
17
|
+
|
|
18
|
+
var properties: DropShadowNodeProperties
|
|
19
|
+
|
|
20
|
+
var propertyMap: any NodePropertyMap {
|
|
21
|
+
properties
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
func applyEffect(to layer: CALayer) {
|
|
25
|
+
if let opacity = properties.opacity?.value.cgFloatValue {
|
|
26
|
+
// Lottie animation files express opacity as a numerical percentage value
|
|
27
|
+
// (e.g. 0%, 50%, 100%) so we divide by 100 to get the decimal values
|
|
28
|
+
// expected by Core Animation (e.g. 0.0, 0.5, 1.0).
|
|
29
|
+
layer.shadowOpacity = Float(opacity / 100)
|
|
30
|
+
}
|
|
31
|
+
if
|
|
32
|
+
let angleDegrees = properties.angle?.value.cgFloatValue,
|
|
33
|
+
let distance = properties.distance?.value.cgFloatValue
|
|
34
|
+
{
|
|
35
|
+
// Lottie animation files express rotation in degrees
|
|
36
|
+
// (e.g. 90º, 180º, 360º) so we convert to radians to get the
|
|
37
|
+
// values expected by Core Animation (e.g. π/2, π, 2π)
|
|
38
|
+
let angleRadians = (angleDegrees * .pi) / 180
|
|
39
|
+
|
|
40
|
+
// Lottie animation files express the `shadowOffset` as (angle, distance) pair,
|
|
41
|
+
// which we convert to the expected x / y offset values:
|
|
42
|
+
let offsetX = distance * cos(angleRadians)
|
|
43
|
+
let offsetY = distance * sin(angleRadians)
|
|
44
|
+
layer.shadowOffset = .init(width: offsetX, height: offsetY)
|
|
45
|
+
}
|
|
46
|
+
layer.shadowColor = properties.color?.value.cgColorValue
|
|
47
|
+
layer.shadowRadius = properties.radius?.value.cgFloatValue ?? 0
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
// MARK: - DropShadowNodeProperties
|
|
53
|
+
|
|
54
|
+
final class DropShadowNodeProperties: NodePropertyMap {
|
|
55
|
+
|
|
56
|
+
// MARK: Lifecycle
|
|
57
|
+
|
|
58
|
+
init(model: DropShadowModel) {
|
|
59
|
+
if let opacityKeyframes = model._opacity?.keyframes {
|
|
60
|
+
opacity = NodeProperty(provider: KeyframeInterpolator(keyframes: opacityKeyframes))
|
|
61
|
+
propertyMap[PropertyName.opacity.rawValue] = opacity
|
|
62
|
+
} else {
|
|
63
|
+
opacity = nil
|
|
64
|
+
}
|
|
65
|
+
if let radiusKeyframes = model._radius?.keyframes {
|
|
66
|
+
radius = NodeProperty(provider: KeyframeInterpolator(keyframes: radiusKeyframes))
|
|
67
|
+
propertyMap["Radius"] = radius
|
|
68
|
+
} else {
|
|
69
|
+
radius = nil
|
|
70
|
+
}
|
|
71
|
+
if let colorKeyFrames = model._color?.keyframes {
|
|
72
|
+
color = NodeProperty(provider: KeyframeInterpolator(keyframes: colorKeyFrames))
|
|
73
|
+
propertyMap[PropertyName.color.rawValue] = color
|
|
74
|
+
} else {
|
|
75
|
+
color = nil
|
|
76
|
+
}
|
|
77
|
+
if let angleKeyFrames = model._angle?.keyframes {
|
|
78
|
+
angle = NodeProperty(provider: KeyframeInterpolator(keyframes: angleKeyFrames))
|
|
79
|
+
propertyMap["Angle"] = angle
|
|
80
|
+
} else {
|
|
81
|
+
angle = nil
|
|
82
|
+
}
|
|
83
|
+
if let distanceKeyFrame = model._distance?.keyframes {
|
|
84
|
+
distance = NodeProperty(provider: KeyframeInterpolator(keyframes: distanceKeyFrame))
|
|
85
|
+
propertyMap["Distance"] = distance
|
|
86
|
+
} else {
|
|
87
|
+
distance = nil
|
|
88
|
+
}
|
|
89
|
+
properties = Array(propertyMap.values)
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
// MARK: Internal
|
|
93
|
+
|
|
94
|
+
var propertyMap: [String: AnyNodeProperty] = [:]
|
|
95
|
+
var properties: [AnyNodeProperty]
|
|
96
|
+
|
|
97
|
+
let opacity: NodeProperty<LottieVector1D>?
|
|
98
|
+
let radius: NodeProperty<LottieVector1D>?
|
|
99
|
+
let color: NodeProperty<LottieColor>?
|
|
100
|
+
let angle: NodeProperty<LottieVector1D>?
|
|
101
|
+
let distance: NodeProperty<LottieVector1D>?
|
|
102
|
+
}
|
package/Sources/Private/MainThread/NodeRenderSystem/Nodes/LayerEffectNodes/LayerEffectNode.swift
ADDED
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
// Created by Lan Xu on 2024/6/8.
|
|
2
|
+
// Copyright © 2024 Airbnb Inc. All rights reserved.
|
|
3
|
+
|
|
4
|
+
import QuartzCore
|
|
5
|
+
|
|
6
|
+
// MARK: - LayerEffectNode
|
|
7
|
+
|
|
8
|
+
protocol LayerEffectNode {
|
|
9
|
+
func applyEffect(to layer: CALayer)
|
|
10
|
+
var propertyMap: NodePropertyMap { get }
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
extension LayerEffectNode {
|
|
14
|
+
|
|
15
|
+
func updateWithFrame(layer: CALayer, frame: CGFloat) {
|
|
16
|
+
for property in propertyMap.properties {
|
|
17
|
+
if property.needsUpdate(frame: frame) {
|
|
18
|
+
property.update(frame: frame)
|
|
19
|
+
}
|
|
20
|
+
}
|
|
21
|
+
applyEffect(to: layer)
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
}
|
package/Sources/Private/MainThread/NodeRenderSystem/Nodes/OutputNodes/Renderables/FillRenderer.swift
CHANGED
|
@@ -11,18 +11,18 @@ extension FillRule {
|
|
|
11
11
|
var cgFillRule: CGPathFillRule {
|
|
12
12
|
switch self {
|
|
13
13
|
case .evenOdd:
|
|
14
|
-
|
|
14
|
+
.evenOdd
|
|
15
15
|
default:
|
|
16
|
-
|
|
16
|
+
.winding
|
|
17
17
|
}
|
|
18
18
|
}
|
|
19
19
|
|
|
20
20
|
var caFillRule: CAShapeLayerFillRule {
|
|
21
21
|
switch self {
|
|
22
22
|
case .evenOdd:
|
|
23
|
-
|
|
23
|
+
CAShapeLayerFillRule.evenOdd
|
|
24
24
|
default:
|
|
25
|
-
|
|
25
|
+
CAShapeLayerFillRule.nonZero
|
|
26
26
|
}
|
|
27
27
|
}
|
|
28
28
|
}
|
|
@@ -11,26 +11,26 @@ extension LineJoin {
|
|
|
11
11
|
var cgLineJoin: CGLineJoin {
|
|
12
12
|
switch self {
|
|
13
13
|
case .bevel:
|
|
14
|
-
|
|
14
|
+
.bevel
|
|
15
15
|
case .none:
|
|
16
|
-
|
|
16
|
+
.miter
|
|
17
17
|
case .miter:
|
|
18
|
-
|
|
18
|
+
.miter
|
|
19
19
|
case .round:
|
|
20
|
-
|
|
20
|
+
.round
|
|
21
21
|
}
|
|
22
22
|
}
|
|
23
23
|
|
|
24
24
|
var caLineJoin: CAShapeLayerLineJoin {
|
|
25
25
|
switch self {
|
|
26
26
|
case .none:
|
|
27
|
-
|
|
27
|
+
CAShapeLayerLineJoin.miter
|
|
28
28
|
case .miter:
|
|
29
|
-
|
|
29
|
+
CAShapeLayerLineJoin.miter
|
|
30
30
|
case .round:
|
|
31
|
-
|
|
31
|
+
CAShapeLayerLineJoin.round
|
|
32
32
|
case .bevel:
|
|
33
|
-
|
|
33
|
+
CAShapeLayerLineJoin.bevel
|
|
34
34
|
}
|
|
35
35
|
}
|
|
36
36
|
}
|
|
@@ -39,26 +39,26 @@ extension LineCap {
|
|
|
39
39
|
var cgLineCap: CGLineCap {
|
|
40
40
|
switch self {
|
|
41
41
|
case .none:
|
|
42
|
-
|
|
42
|
+
.butt
|
|
43
43
|
case .butt:
|
|
44
|
-
|
|
44
|
+
.butt
|
|
45
45
|
case .round:
|
|
46
|
-
|
|
46
|
+
.round
|
|
47
47
|
case .square:
|
|
48
|
-
|
|
48
|
+
.square
|
|
49
49
|
}
|
|
50
50
|
}
|
|
51
51
|
|
|
52
52
|
var caLineCap: CAShapeLayerLineCap {
|
|
53
53
|
switch self {
|
|
54
54
|
case .none:
|
|
55
|
-
|
|
55
|
+
CAShapeLayerLineCap.butt
|
|
56
56
|
case .butt:
|
|
57
|
-
|
|
57
|
+
CAShapeLayerLineCap.butt
|
|
58
58
|
case .round:
|
|
59
|
-
|
|
59
|
+
CAShapeLayerLineCap.round
|
|
60
60
|
case .square:
|
|
61
|
-
|
|
61
|
+
CAShapeLayerLineCap.square
|
|
62
62
|
}
|
|
63
63
|
}
|
|
64
64
|
}
|
|
@@ -122,7 +122,7 @@ extension BezierPath {
|
|
|
122
122
|
var vertices = [CurveVertex(point: point + position, inTangentRelative: .zero, outTangentRelative: .zero)]
|
|
123
123
|
|
|
124
124
|
var previousPoint = point
|
|
125
|
-
currentAngle += anglePerPoint
|
|
125
|
+
currentAngle += anglePerPoint
|
|
126
126
|
for _ in 0..<Int(ceil(numberOfPoints)) {
|
|
127
127
|
previousPoint = point
|
|
128
128
|
point = CGPoint(
|
|
@@ -131,8 +131,8 @@ extension BezierPath {
|
|
|
131
131
|
|
|
132
132
|
if outerRoundedness != 0 {
|
|
133
133
|
let cp1Theta = (atan2(previousPoint.y, previousPoint.x) - CGFloat.pi / 2)
|
|
134
|
-
let cp1Dx = cos(cp1Theta)
|
|
135
|
-
let cp1Dy = sin(cp1Theta)
|
|
134
|
+
let cp1Dx = cos(cp1Theta)
|
|
135
|
+
let cp1Dy = sin(cp1Theta)
|
|
136
136
|
|
|
137
137
|
let cp2Theta = (atan2(point.y, point.x) - CGFloat.pi / 2)
|
|
138
138
|
let cp2Dx = cos(cp2Theta)
|
|
@@ -154,7 +154,7 @@ extension BezierPath {
|
|
|
154
154
|
} else {
|
|
155
155
|
vertices.append(CurveVertex(point: point + position, inTangentRelative: .zero, outTangentRelative: .zero))
|
|
156
156
|
}
|
|
157
|
-
currentAngle += anglePerPoint
|
|
157
|
+
currentAngle += anglePerPoint
|
|
158
158
|
}
|
|
159
159
|
let reverse = direction == .counterClockwise
|
|
160
160
|
if reverse {
|
|
@@ -169,11 +169,11 @@ extension [DashElement] {
|
|
|
169
169
|
}
|
|
170
170
|
|
|
171
171
|
extension [CGFloat] {
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
172
|
+
/// If all of the items in the dash pattern are zeros, then we shouldn't attempt to render it.
|
|
173
|
+
/// This causes Core Animation to have extremely poor performance for some reason, even though
|
|
174
|
+
/// it doesn't affect the appearance of the animation.
|
|
175
|
+
/// - We check for `== 0.01` instead of `== 0` because `dashPattern.shapeLayerConfiguration`
|
|
176
|
+
/// converts all `0` values to `0.01` to work around a different Core Animation rendering issue.
|
|
177
177
|
var isSupportedLayerDashPattern: Bool {
|
|
178
178
|
!allSatisfy { $0 == 0.01 }
|
|
179
179
|
}
|
|
@@ -15,6 +15,7 @@ final class TextAnimatorNodeProperties: NodePropertyMap, KeypathSearchable {
|
|
|
15
15
|
|
|
16
16
|
init(textAnimator: TextAnimator) {
|
|
17
17
|
keypathName = textAnimator.name
|
|
18
|
+
textRangeUnit = textAnimator.textRangeUnit
|
|
18
19
|
var properties = [String : AnyNodeProperty]()
|
|
19
20
|
|
|
20
21
|
if let keyframeGroup = textAnimator.anchor {
|
|
@@ -109,6 +110,27 @@ final class TextAnimatorNodeProperties: NodePropertyMap, KeypathSearchable {
|
|
|
109
110
|
tracking = nil
|
|
110
111
|
}
|
|
111
112
|
|
|
113
|
+
if let startKeyframes = textAnimator.start {
|
|
114
|
+
start = NodeProperty(provider: KeyframeInterpolator(keyframes: startKeyframes.keyframes))
|
|
115
|
+
properties["Start"] = start
|
|
116
|
+
} else {
|
|
117
|
+
start = nil
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
if let endKeyframes = textAnimator.end {
|
|
121
|
+
end = NodeProperty(provider: KeyframeInterpolator(keyframes: endKeyframes.keyframes))
|
|
122
|
+
properties["End"] = end
|
|
123
|
+
} else {
|
|
124
|
+
end = nil
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
if let selectedRangeOpacityKeyframes = textAnimator.opacity {
|
|
128
|
+
selectedRangeOpacity = NodeProperty(provider: KeyframeInterpolator(keyframes: selectedRangeOpacityKeyframes.keyframes))
|
|
129
|
+
properties["SelectedRangeOpacity"] = selectedRangeOpacity
|
|
130
|
+
} else {
|
|
131
|
+
selectedRangeOpacity = nil
|
|
132
|
+
}
|
|
133
|
+
|
|
112
134
|
keypathProperties = properties
|
|
113
135
|
|
|
114
136
|
self.properties = Array(keypathProperties.values)
|
|
@@ -131,6 +153,10 @@ final class TextAnimatorNodeProperties: NodePropertyMap, KeypathSearchable {
|
|
|
131
153
|
let fillColor: NodeProperty<LottieColor>?
|
|
132
154
|
let strokeWidth: NodeProperty<LottieVector1D>?
|
|
133
155
|
let tracking: NodeProperty<LottieVector1D>?
|
|
156
|
+
let start: NodeProperty<LottieVector1D>?
|
|
157
|
+
let end: NodeProperty<LottieVector1D>?
|
|
158
|
+
let selectedRangeOpacity: NodeProperty<LottieVector1D>?
|
|
159
|
+
let textRangeUnit: TextRangeUnit?
|
|
134
160
|
|
|
135
161
|
let keypathProperties: [String: AnyNodeProperty]
|
|
136
162
|
let properties: [AnyNodeProperty]
|
|
@@ -223,6 +249,33 @@ final class TextOutputNode: NodeOutput {
|
|
|
223
249
|
}
|
|
224
250
|
}
|
|
225
251
|
|
|
252
|
+
var start: CGFloat? {
|
|
253
|
+
get {
|
|
254
|
+
_start
|
|
255
|
+
}
|
|
256
|
+
set {
|
|
257
|
+
_start = newValue
|
|
258
|
+
}
|
|
259
|
+
}
|
|
260
|
+
|
|
261
|
+
var end: CGFloat? {
|
|
262
|
+
get {
|
|
263
|
+
_end
|
|
264
|
+
}
|
|
265
|
+
set {
|
|
266
|
+
_end = newValue
|
|
267
|
+
}
|
|
268
|
+
}
|
|
269
|
+
|
|
270
|
+
var selectedRangeOpacity: CGFloat? {
|
|
271
|
+
get {
|
|
272
|
+
_selectedRangeOpacity
|
|
273
|
+
}
|
|
274
|
+
set {
|
|
275
|
+
_selectedRangeOpacity = newValue
|
|
276
|
+
}
|
|
277
|
+
}
|
|
278
|
+
|
|
226
279
|
func hasOutputUpdates(_: CGFloat) -> Bool {
|
|
227
280
|
// TODO Fix This
|
|
228
281
|
true
|
|
@@ -236,6 +289,9 @@ final class TextOutputNode: NodeOutput {
|
|
|
236
289
|
fileprivate var _fillColor: CGColor?
|
|
237
290
|
fileprivate var _tracking: CGFloat?
|
|
238
291
|
fileprivate var _strokeWidth: CGFloat?
|
|
292
|
+
fileprivate var _start: CGFloat?
|
|
293
|
+
fileprivate var _end: CGFloat?
|
|
294
|
+
fileprivate var _selectedRangeOpacity: CGFloat?
|
|
239
295
|
}
|
|
240
296
|
|
|
241
297
|
// MARK: - TextAnimatorNode
|
|
@@ -278,10 +334,13 @@ class TextAnimatorNode: AnimatorNode {
|
|
|
278
334
|
|
|
279
335
|
func rebuildOutputs(frame _: CGFloat) {
|
|
280
336
|
textOutputNode.xform = textAnimatorProperties.caTransform
|
|
281
|
-
textOutputNode.opacity =
|
|
337
|
+
textOutputNode.opacity = 1.0
|
|
282
338
|
textOutputNode.strokeColor = textAnimatorProperties.strokeColor?.value.cgColorValue
|
|
283
339
|
textOutputNode.fillColor = textAnimatorProperties.fillColor?.value.cgColorValue
|
|
284
340
|
textOutputNode.tracking = textAnimatorProperties.tracking?.value.cgFloatValue ?? 1
|
|
285
341
|
textOutputNode.strokeWidth = textAnimatorProperties.strokeWidth?.value.cgFloatValue ?? 0
|
|
342
|
+
textOutputNode.start = textAnimatorProperties.start?.value.cgFloatValue
|
|
343
|
+
textOutputNode.end = textAnimatorProperties.end?.value.cgFloatValue
|
|
344
|
+
textOutputNode.selectedRangeOpacity = (textAnimatorProperties.opacity?.value.cgFloatValue).flatMap { $0 * 0.01 }
|
|
286
345
|
}
|
|
287
346
|
}
|
|
@@ -29,9 +29,9 @@ struct DotLottieAnimation: Codable {
|
|
|
29
29
|
var loopMode: LottieLoopMode {
|
|
30
30
|
switch mode {
|
|
31
31
|
case .bounce:
|
|
32
|
-
|
|
32
|
+
.autoReverse
|
|
33
33
|
case .normal, nil:
|
|
34
|
-
|
|
34
|
+
(loop ?? false) ? .loop : .playOnce
|
|
35
35
|
}
|
|
36
36
|
}
|
|
37
37
|
|
|
@@ -151,12 +151,15 @@ extension KeyframeGroup: DictionaryInitializable where T: AnyInitializable {
|
|
|
151
151
|
{
|
|
152
152
|
keyframes = [Keyframe<T>(value)]
|
|
153
153
|
} else {
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
154
|
+
let frameDictionaries: [[String: Any]] =
|
|
155
|
+
if
|
|
156
|
+
let singleFrameDictionary =
|
|
157
|
+
dictionary[KeyframeWrapperKey.keyframeData.rawValue] as? [String: Any]
|
|
158
|
+
{
|
|
159
|
+
[singleFrameDictionary]
|
|
160
|
+
} else {
|
|
161
|
+
try dictionary.value(for: KeyframeWrapperKey.keyframeData)
|
|
162
|
+
}
|
|
160
163
|
var previousKeyframeData: KeyframeData<T>?
|
|
161
164
|
for frameDictionary in frameDictionaries {
|
|
162
165
|
let data = try KeyframeData<T>(dictionary: frameDictionary)
|
|
@@ -23,14 +23,14 @@ extension EffectValueType: ClassFamily {
|
|
|
23
23
|
func getType() -> AnyObject.Type {
|
|
24
24
|
switch self {
|
|
25
25
|
case .slider:
|
|
26
|
-
|
|
26
|
+
Vector1DEffectValue.self
|
|
27
27
|
case .angle:
|
|
28
|
-
|
|
28
|
+
Vector1DEffectValue.self
|
|
29
29
|
case .color:
|
|
30
|
-
|
|
30
|
+
ColorEffectValue.self
|
|
31
31
|
case .unknown:
|
|
32
32
|
// Unsupported
|
|
33
|
-
|
|
33
|
+
LayerEffect.self
|
|
34
34
|
}
|
|
35
35
|
}
|
|
36
36
|
}
|
|
@@ -21,10 +21,10 @@ extension LayerEffectType: ClassFamily {
|
|
|
21
21
|
func getType() -> AnyObject.Type {
|
|
22
22
|
switch self {
|
|
23
23
|
case .dropShadow:
|
|
24
|
-
|
|
24
|
+
DropShadowEffect.self
|
|
25
25
|
case .unknown:
|
|
26
26
|
// Unsupported
|
|
27
|
-
|
|
27
|
+
LayerEffect.self
|
|
28
28
|
}
|
|
29
29
|
}
|
|
30
30
|
}
|
|
@@ -20,10 +20,10 @@ extension LayerStyleType: ClassFamily {
|
|
|
20
20
|
func getType() -> AnyObject.Type {
|
|
21
21
|
switch self {
|
|
22
22
|
case .dropShadow:
|
|
23
|
-
|
|
23
|
+
DropShadowStyle.self
|
|
24
24
|
case .unknown:
|
|
25
25
|
// Unsupported
|
|
26
|
-
|
|
26
|
+
LayerStyle.self
|
|
27
27
|
}
|
|
28
28
|
}
|
|
29
29
|
}
|
|
@@ -14,19 +14,19 @@ extension LayerType: ClassFamily {
|
|
|
14
14
|
func getType() -> AnyObject.Type {
|
|
15
15
|
switch self {
|
|
16
16
|
case .precomp:
|
|
17
|
-
|
|
17
|
+
PreCompLayerModel.self
|
|
18
18
|
case .solid:
|
|
19
|
-
|
|
19
|
+
SolidLayerModel.self
|
|
20
20
|
case .image:
|
|
21
|
-
|
|
21
|
+
ImageLayerModel.self
|
|
22
22
|
case .null:
|
|
23
|
-
|
|
23
|
+
LayerModel.self
|
|
24
24
|
case .shape:
|
|
25
|
-
|
|
25
|
+
ShapeLayerModel.self
|
|
26
26
|
case .text:
|
|
27
|
-
|
|
27
|
+
TextLayerModel.self
|
|
28
28
|
case .unknown:
|
|
29
|
-
|
|
29
|
+
LayerModel.self
|
|
30
30
|
}
|
|
31
31
|
}
|
|
32
32
|
}
|
|
@@ -38,35 +38,35 @@ extension ShapeType: ClassFamily {
|
|
|
38
38
|
func getType() -> AnyObject.Type {
|
|
39
39
|
switch self {
|
|
40
40
|
case .ellipse:
|
|
41
|
-
|
|
41
|
+
Ellipse.self
|
|
42
42
|
case .fill:
|
|
43
|
-
|
|
43
|
+
Fill.self
|
|
44
44
|
case .gradientFill:
|
|
45
|
-
|
|
45
|
+
GradientFill.self
|
|
46
46
|
case .group:
|
|
47
|
-
|
|
47
|
+
Group.self
|
|
48
48
|
case .gradientStroke:
|
|
49
|
-
|
|
49
|
+
GradientStroke.self
|
|
50
50
|
case .merge:
|
|
51
|
-
|
|
51
|
+
Merge.self
|
|
52
52
|
case .rectangle:
|
|
53
|
-
|
|
53
|
+
Rectangle.self
|
|
54
54
|
case .repeater:
|
|
55
|
-
|
|
55
|
+
Repeater.self
|
|
56
56
|
case .round:
|
|
57
|
-
|
|
57
|
+
RoundedCorners.self
|
|
58
58
|
case .shape:
|
|
59
|
-
|
|
59
|
+
Shape.self
|
|
60
60
|
case .star:
|
|
61
|
-
|
|
61
|
+
Star.self
|
|
62
62
|
case .stroke:
|
|
63
|
-
|
|
63
|
+
Stroke.self
|
|
64
64
|
case .trim:
|
|
65
|
-
|
|
65
|
+
Trim.self
|
|
66
66
|
case .transform:
|
|
67
|
-
|
|
67
|
+
ShapeTransform.self
|
|
68
68
|
default:
|
|
69
|
-
|
|
69
|
+
ShapeItem.self
|
|
70
70
|
}
|
|
71
71
|
}
|
|
72
72
|
}
|
|
@@ -5,6 +5,15 @@
|
|
|
5
5
|
// Created by Brandon Withrow on 1/9/19.
|
|
6
6
|
//
|
|
7
7
|
|
|
8
|
+
// MARK: - TextRangeUnit
|
|
9
|
+
|
|
10
|
+
enum TextRangeUnit: Int, RawRepresentable, Codable {
|
|
11
|
+
case percentage = 1
|
|
12
|
+
case index = 2
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
// MARK: - TextAnimator
|
|
16
|
+
|
|
8
17
|
final class TextAnimator: Codable, DictionaryInitializable {
|
|
9
18
|
|
|
10
19
|
// MARK: Lifecycle
|
|
@@ -12,6 +21,7 @@ final class TextAnimator: Codable, DictionaryInitializable {
|
|
|
12
21
|
required init(from decoder: Decoder) throws {
|
|
13
22
|
let container = try decoder.container(keyedBy: TextAnimator.CodingKeys.self)
|
|
14
23
|
name = try container.decodeIfPresent(String.self, forKey: .name) ?? ""
|
|
24
|
+
|
|
15
25
|
let animatorContainer = try container.nestedContainer(keyedBy: TextAnimatorKeys.self, forKey: .textAnimator)
|
|
16
26
|
fillColor = try animatorContainer.decodeIfPresent(KeyframeGroup<LottieColor>.self, forKey: .fillColor)
|
|
17
27
|
strokeColor = try animatorContainer.decodeIfPresent(KeyframeGroup<LottieColor>.self, forKey: .strokeColor)
|
|
@@ -32,10 +42,16 @@ final class TextAnimator: Codable, DictionaryInitializable {
|
|
|
32
42
|
rotationZ = nil
|
|
33
43
|
}
|
|
34
44
|
opacity = try animatorContainer.decodeIfPresent(KeyframeGroup<LottieVector1D>.self, forKey: .opacity)
|
|
45
|
+
|
|
46
|
+
let selectorContainer = try? container.nestedContainer(keyedBy: TextSelectorKeys.self, forKey: .textSelector)
|
|
47
|
+
start = try? selectorContainer?.decodeIfPresent(KeyframeGroup<LottieVector1D>.self, forKey: .start)
|
|
48
|
+
end = try? selectorContainer?.decodeIfPresent(KeyframeGroup<LottieVector1D>.self, forKey: .end)
|
|
49
|
+
textRangeUnit = try? selectorContainer?.decodeIfPresent(TextRangeUnit.self, forKey: .textRangeUnits)
|
|
35
50
|
}
|
|
36
51
|
|
|
37
52
|
init(dictionary: [String: Any]) throws {
|
|
38
53
|
name = (try? dictionary.value(for: CodingKeys.name)) ?? ""
|
|
54
|
+
|
|
39
55
|
let animatorDictionary: [String: Any] = try dictionary.value(for: CodingKeys.textAnimator)
|
|
40
56
|
if let fillColorDictionary = animatorDictionary[TextAnimatorKeys.fillColor.rawValue] as? [String: Any] {
|
|
41
57
|
fillColor = try? KeyframeGroup<LottieColor>(dictionary: fillColorDictionary)
|
|
@@ -107,6 +123,26 @@ final class TextAnimator: Codable, DictionaryInitializable {
|
|
|
107
123
|
} else {
|
|
108
124
|
opacity = nil
|
|
109
125
|
}
|
|
126
|
+
|
|
127
|
+
let selectorDictionary: [String: Any] = try dictionary.value(for: CodingKeys.textSelector)
|
|
128
|
+
|
|
129
|
+
if let startDictionary = selectorDictionary[TextSelectorKeys.start.rawValue] as? [String: Any] {
|
|
130
|
+
start = try KeyframeGroup<LottieVector1D>(dictionary: startDictionary)
|
|
131
|
+
} else {
|
|
132
|
+
start = nil
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
if let endDictionary = selectorDictionary[TextSelectorKeys.end.rawValue] as? [String: Any] {
|
|
136
|
+
end = try KeyframeGroup<LottieVector1D>(dictionary: endDictionary)
|
|
137
|
+
} else {
|
|
138
|
+
end = nil
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
if let textRangeUnitValue = selectorDictionary[TextSelectorKeys.textRangeUnits.rawValue] as? Int {
|
|
142
|
+
textRangeUnit = TextRangeUnit(rawValue: textRangeUnitValue)
|
|
143
|
+
} else {
|
|
144
|
+
textRangeUnit = nil
|
|
145
|
+
}
|
|
110
146
|
}
|
|
111
147
|
|
|
112
148
|
// MARK: Internal
|
|
@@ -152,6 +188,15 @@ final class TextAnimator: Codable, DictionaryInitializable {
|
|
|
152
188
|
/// Tracking
|
|
153
189
|
let tracking: KeyframeGroup<LottieVector1D>?
|
|
154
190
|
|
|
191
|
+
/// Start
|
|
192
|
+
let start: KeyframeGroup<LottieVector1D>?
|
|
193
|
+
|
|
194
|
+
/// End
|
|
195
|
+
let end: KeyframeGroup<LottieVector1D>?
|
|
196
|
+
|
|
197
|
+
/// The type of unit used by the start/end ranges
|
|
198
|
+
let textRangeUnit: TextRangeUnit?
|
|
199
|
+
|
|
155
200
|
func encode(to encoder: Encoder) throws {
|
|
156
201
|
var container = encoder.container(keyedBy: CodingKeys.self)
|
|
157
202
|
var animatorContainer = container.nestedContainer(keyedBy: TextAnimatorKeys.self, forKey: .textAnimator)
|
|
@@ -164,7 +209,7 @@ final class TextAnimator: Codable, DictionaryInitializable {
|
|
|
164
209
|
// MARK: Private
|
|
165
210
|
|
|
166
211
|
private enum CodingKeys: String, CodingKey {
|
|
167
|
-
|
|
212
|
+
case textSelector = "s"
|
|
168
213
|
case textAnimator = "a"
|
|
169
214
|
case name = "nm"
|
|
170
215
|
}
|
|
@@ -173,6 +218,7 @@ final class TextAnimator: Codable, DictionaryInitializable {
|
|
|
173
218
|
case start = "s"
|
|
174
219
|
case end = "e"
|
|
175
220
|
case offset = "o"
|
|
221
|
+
case textRangeUnits = "r"
|
|
176
222
|
}
|
|
177
223
|
|
|
178
224
|
private enum TextAnimatorKeys: String, CodingKey {
|
|
@@ -95,12 +95,12 @@ extension CALayer {
|
|
|
95
95
|
}
|
|
96
96
|
|
|
97
97
|
if visible {
|
|
98
|
-
let style: LayerDebugStyle
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
98
|
+
let style: LayerDebugStyle =
|
|
99
|
+
if let layerDebugging = self as? LayerDebugging {
|
|
100
|
+
layerDebugging.debugStyle
|
|
101
|
+
} else {
|
|
102
|
+
LayerDebugStyle.defaultStyle()
|
|
103
|
+
}
|
|
104
104
|
let debugLayer = DebugLayer(style: style)
|
|
105
105
|
var container = self
|
|
106
106
|
if let cust = self as? CustomLayerDebugging {
|