Skip to content

Commit

Permalink
Add support for customizing gradient values using GradientValueProvid…
Browse files Browse the repository at this point in the history
…er (#2182)
  • Loading branch information
calda authored Sep 14, 2023
1 parent f7d758c commit ccd7a10
Show file tree
Hide file tree
Showing 20 changed files with 69 additions and 22 deletions.
45 changes: 35 additions & 10 deletions Sources/Private/CoreAnimation/Animations/LayerProperty.swift
Original file line number Diff line number Diff line change
Expand Up @@ -62,7 +62,10 @@ struct CustomizableProperty<ValueRepresentation> {

/// A closure that coverts the type-erased value of an `AnyValueProvider`
/// to the strongly-typed representation used by this property, if possible.
let conversion: (Any) -> ValueRepresentation?
/// - `value` is the value for the current frame that should be converted,
/// as returned by `AnyValueProvider.typeErasedStorage`.
/// - `valueProvider` is the `AnyValueProvider` that returned the type-erased value.
let conversion: (_ value: Any, _ valueProvider: AnyValueProvider) -> ValueRepresentation?
}

// MARK: - PropertyName
Expand All @@ -77,6 +80,7 @@ enum PropertyName: String, CaseIterable {
case position = "Position"
case rotation = "Rotation"
case strokeWidth = "Stroke Width"
case gradientColors = "Colors"
}

// MARK: CALayer properties
Expand Down Expand Up @@ -260,14 +264,14 @@ extension LayerProperty {
.init(
caLayerKeypath: #keyPath(CAGradientLayer.colors),
defaultValue: nil,
customizableProperty: nil /* currently unsupported */ )
customizableProperty: .gradientColors)
}

static var locations: LayerProperty<[CGFloat]> {
.init(
caLayerKeypath: #keyPath(CAGradientLayer.locations),
defaultValue: nil,
customizableProperty: nil /* currently unsupported */ )
customizableProperty: .gradientLocations)
}

static var startPoint: LayerProperty<CGPoint> {
Expand All @@ -291,7 +295,7 @@ extension CustomizableProperty {
static var color: CustomizableProperty<CGColor> {
.init(
name: [.color],
conversion: { typeErasedValue in
conversion: { typeErasedValue, _ in
guard let color = typeErasedValue as? LottieColor else {
return nil
}
Expand All @@ -303,7 +307,7 @@ extension CustomizableProperty {
static var opacity: CustomizableProperty<CGFloat> {
.init(
name: [.opacity],
conversion: { typeErasedValue in
conversion: { typeErasedValue, _ in
guard let vector = typeErasedValue as? LottieVector1D else { return nil }

// Lottie animation files express opacity as a numerical percentage value
Expand All @@ -316,7 +320,7 @@ extension CustomizableProperty {
static var scaleX: CustomizableProperty<CGFloat> {
.init(
name: [.scale],
conversion: { typeErasedValue in
conversion: { typeErasedValue, _ in
guard let vector = typeErasedValue as? LottieVector3D else { return nil }

// Lottie animation files express scale as a numerical percentage value
Expand All @@ -329,7 +333,7 @@ extension CustomizableProperty {
static var scaleY: CustomizableProperty<CGFloat> {
.init(
name: [.scale],
conversion: { typeErasedValue in
conversion: { typeErasedValue, _ in
guard let vector = typeErasedValue as? LottieVector3D else { return nil }

// Lottie animation files express scale as a numerical percentage value
Expand All @@ -342,7 +346,7 @@ extension CustomizableProperty {
static var rotation: CustomizableProperty<CGFloat> {
.init(
name: [.rotation],
conversion: { typeErasedValue in
conversion: { typeErasedValue, _ in
guard let vector = typeErasedValue as? LottieVector1D else { return nil }

// Lottie animation files express rotation in degrees
Expand All @@ -355,13 +359,34 @@ extension CustomizableProperty {
static var position: CustomizableProperty<CGPoint> {
.init(
name: [.position],
conversion: { ($0 as? LottieVector3D)?.pointValue })
conversion: { typeErasedValue, _ in
guard let vector = typeErasedValue as? LottieVector3D else { return nil }
return vector.pointValue
})
}

static var gradientColors: CustomizableProperty<[CGColor]> {
.init(
name: [.gradientColors],
conversion: { _, typeErasedValueProvider in
guard let gradientValueProvider = typeErasedValueProvider as? GradientValueProvider else { return nil }
return gradientValueProvider.colors.map { $0.cgColorValue }
})
}

static var gradientLocations: CustomizableProperty<[CGFloat]> {
.init(
name: [.gradientColors],
conversion: { _, typeErasedValueProvider in
guard let gradientValueProvider = typeErasedValueProvider as? GradientValueProvider else { return nil }
return gradientValueProvider.locations.map { CGFloat($0) }
})
}

static func floatValue(_ name: PropertyName...) -> CustomizableProperty<CGFloat> {
.init(
name: name,
conversion: { typeErasedValue in
conversion: { typeErasedValue, _ in
guard let vector = typeErasedValue as? LottieVector1D else { return nil }
return vector.cgFloatValue
})
Expand Down
6 changes: 4 additions & 2 deletions Sources/Private/CoreAnimation/ValueProviderStore.swift
Original file line number Diff line number Diff line change
Expand Up @@ -73,9 +73,11 @@ final class ValueProviderStore {

// Convert the type-erased keyframe values using this `CustomizableProperty`'s conversion closure
let typedKeyframes = typeErasedKeyframes.compactMap { typeErasedKeyframe -> Keyframe<Value>? in
guard let convertedValue = customizableProperty.conversion(typeErasedKeyframe.value) else {
guard let convertedValue = customizableProperty.conversion(typeErasedKeyframe.value, anyValueProvider) else {
logger.assertionFailure("""
Could not convert value of type \(type(of: typeErasedKeyframe.value)) to expected type \(Value.self)
Could not convert value of type \(type(of: typeErasedKeyframe.value)) from \(anyValueProvider) to expected type \(
Value
.self)
""")
return nil
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@ final class GradientFillProperties: NodePropertyMap, KeypathSearchable {
PropertyName.opacity.rawValue : opacity,
"Start Point" : startPoint,
"End Point" : endPoint,
"Colors" : colors,
PropertyName.gradientColors.rawValue : colors,
]
properties = Array(keypathProperties.values)
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -47,7 +47,7 @@ final class GradientStrokeProperties: NodePropertyMap, KeypathSearchable {
PropertyName.opacity.rawValue : opacity,
"Start Point" : startPoint,
"End Point" : endPoint,
"Colors" : colors,
PropertyName.gradientColors.rawValue : colors,
PropertyName.strokeWidth.rawValue : width,
"Dashes" : dashPattern,
"Dash Phase" : dashPhase,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -69,16 +69,18 @@ public final class GradientValueProvider: ValueProvider {
}

public var storage: ValueProviderStorage<[Double]> {
.closure { [self] frame in
hasUpdate = false
if let block = block {
return .closure { [self] frame in
hasUpdate = false

if let block = block {
let newColors = block(frame)
let newLocations = locationsBlock?(frame) ?? []
value = value(from: newColors, locations: newLocations)
}

return value
return value
}
} else {
return .singleValue(value)
}
}

Expand Down Expand Up @@ -129,7 +131,9 @@ public final class GradientValueProvider: ValueProvider {

}

extension GradientValueProvider {
// MARK: Equatable

extension GradientValueProvider: Equatable {
public static func ==(_ lhs: GradientValueProvider, _ rhs: GradientValueProvider) -> Bool {
lhs.identity == rhs.identity
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -74,7 +74,9 @@ public final class PointValueProvider: ValueProvider {
private let identity: AnyHashable
}

extension PointValueProvider {
// MARK: Equatable

extension PointValueProvider: Equatable {
public static func ==(_ lhs: PointValueProvider, _ rhs: PointValueProvider) -> Bool {
lhs.identity == rhs.identity
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -74,7 +74,9 @@ public final class SizeValueProvider: ValueProvider {
private let identity: AnyHashable
}

extension SizeValueProvider {
// MARK: Equatable

extension SizeValueProvider: Equatable {
public static func ==(_ lhs: SizeValueProvider, _ rhs: SizeValueProvider) -> Bool {
lhs.identity == rhs.identity
}
Expand Down
1 change: 1 addition & 0 deletions Tests/Samples/Issues/issue_1854.json
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
{"v":"5.7.4","fr":60,"ip":0,"op":68,"w":450,"h":450,"nm":"GX_fab_rings","ddd":0,"assets":[],"layers":[{"ddd":0,"ind":1,"ty":4,"nm":".accent","cl":"accent","sr":1,"ks":{"o":{"a":1,"k":[{"i":{"x":[0.667],"y":[1]},"o":{"x":[0.333],"y":[0]},"t":0,"s":[0]},{"i":{"x":[0.667],"y":[1]},"o":{"x":[0.333],"y":[0]},"t":16,"s":[100]},{"i":{"x":[0.724],"y":[1]},"o":{"x":[0.305],"y":[0]},"t":32,"s":[80]},{"i":{"x":[0.833],"y":[1]},"o":{"x":[0.42],"y":[0]},"t":52,"s":[80]},{"t":66,"s":[0]}],"ix":11},"r":{"a":0,"k":-45,"ix":10},"p":{"a":0,"k":[225,225,0],"ix":2,"l":2},"a":{"a":0,"k":[0,0,0],"ix":1,"l":2},"s":{"a":1,"k":[{"i":{"x":[0.537,0.537,0.667],"y":[1.001,1.001,1]},"o":{"x":[0.333,0.333,0.333],"y":[0,0,0]},"t":0,"s":[13.667,13.667,100]},{"i":{"x":[0.538,0.538,0.667],"y":[0.938,0.938,1]},"o":{"x":[0.586,0.586,0.333],"y":[0.015,0.015,0]},"t":16,"s":[90.667,90.667,100]},{"i":{"x":[0.472,0.472,0.667],"y":[1.057,1.057,1]},"o":{"x":[0.264,0.264,0.333],"y":[-0.013,-0.013,0]},"t":32,"s":[67.712,67.712,100]},{"i":{"x":[0.667,0.667,0.667],"y":[1,1,1]},"o":{"x":[0.333,0.333,0.333],"y":[0,0,0]},"t":50,"s":[74.667,74.667,100]},{"i":{"x":[0.284,0.284,0.667],"y":[1,1,1]},"o":{"x":[0.333,0.333,0.333],"y":[0,0,0]},"t":52,"s":[74.667,74.667,100]},{"t":66,"s":[5.667,5.667,100]}],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"d":1,"ty":"el","s":{"a":0,"k":[450,450],"ix":2},"p":{"a":0,"k":[0,0],"ix":3},"nm":"Ellipse Path 1","mn":"ADBE Vector Shape - Ellipse","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Ellipse 1","np":1,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false},{"ty":"gf","o":{"a":0,"k":100,"ix":10},"r":1,"bm":0,"g":{"p":3,"k":{"a":0,"k":[0,0.98,0.118,0.306,0.5,0.98,0.118,0.306,1,0.98,0.118,0.306,0,1,0.5,0.75,1,0.5],"ix":9}},"s":{"a":0,"k":[0,-180],"ix":5},"e":{"a":0,"k":[0,212],"ix":6},"t":1,"nm":"Gradient Fill 1","mn":"ADBE Vector Graphic - G-Fill","hd":false}],"ip":0,"op":68,"st":0,"bm":0}],"markers":[]}
10 changes: 10 additions & 0 deletions Tests/SnapshotConfiguration.swift
Original file line number Diff line number Diff line change
Expand Up @@ -97,6 +97,16 @@ extension SnapshotConfiguration {
AnimationKeypath(keypath: "**.base_color.**.Color"): ColorValueProvider(.black),
]),

"Issues/issue_1854": .customValueProviders([
AnimationKeypath(keypath: "**.Colors"): GradientValueProvider(
[
LottieColor(r: 0, g: 0, b: 0, a: 0),
LottieColor(r: 1, g: 1, b: 1, a: 0.5),
LottieColor(r: 1, g: 1, b: 1, a: 1),
],
locations: [0, 0.3, 1.0]),
]),

"Issues/issue_1847": .customValueProviders([
AnimationKeypath(keypath: "**.Stroke 1.**.Color"): ColorValueProvider(.red),
]),
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
Supports Core Animation engine
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.

0 comments on commit ccd7a10

Please sign in to comment.