-
-
Notifications
You must be signed in to change notification settings - Fork 5.4k
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
[Compose] Added the ability to set dynamic properties (#1831)
Example usage can be seen on DynamicPropertiesExamplesPage.kt. Thanks to Compose's impressive snapshot system, states that are read during the drawing pass via the dynamic properties callback are automatically registered so invalidation happens correctly by default.
- Loading branch information
Showing
8 changed files
with
335 additions
and
1 deletion.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
156 changes: 156 additions & 0 deletions
156
lottie-compose/src/main/java/com/airbnb/lottie/compose/LottieDynamicProperties.kt
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,156 @@ | ||
package com.airbnb.lottie.compose | ||
|
||
import android.graphics.ColorFilter | ||
import android.graphics.PointF | ||
import androidx.compose.runtime.Composable | ||
import androidx.compose.runtime.getValue | ||
import androidx.compose.runtime.remember | ||
import androidx.compose.runtime.rememberUpdatedState | ||
import com.airbnb.lottie.LottieDrawable | ||
import com.airbnb.lottie.model.KeyPath | ||
import com.airbnb.lottie.value.LottieFrameInfo | ||
import com.airbnb.lottie.value.LottieValueCallback | ||
import com.airbnb.lottie.value.ScaleXY | ||
|
||
/** | ||
* Use this function when you want to apply one or more dynamic properties to an animation. | ||
* This takes a vararg of individual dynamic properties which should be created with [rememberLottieDynamicProperty]. | ||
* | ||
* @see rememberLottieDynamicProperty | ||
*/ | ||
@Composable | ||
fun rememberLottieDynamicProperties( | ||
vararg properties: LottieDynamicProperty<*>, | ||
): LottieDynamicProperties { | ||
return remember(properties) { | ||
LottieDynamicProperties(properties.toList()) | ||
} | ||
} | ||
|
||
/** | ||
* Use this to create a single dynamic property for an animation. | ||
* | ||
* @param property should be one of [com.airbnb.lottie.LottieProperty]. | ||
* @param value the desired value to use as this property's value. | ||
* @param keyPath the string parts of a [com.airbnb.lottie.model.KeyPath] that specify which animation element | ||
* the property resides on. | ||
*/ | ||
@Composable | ||
fun <T> rememberLottieDynamicProperty( | ||
property: T, | ||
value: T, | ||
vararg keyPath: String, | ||
): LottieDynamicProperty<T> { | ||
val keyPathObj = remember(keyPath) { KeyPath(*keyPath) } | ||
return remember(keyPathObj, property, value) { | ||
LottieDynamicProperty(property, keyPathObj, value) | ||
} | ||
} | ||
|
||
/** | ||
* Use this to create a single dynamic property for an animation. | ||
* | ||
* @param property Should be one of [com.airbnb.lottie.LottieProperty]. | ||
* @param keyPath The string parts of a [com.airbnb.lottie.model.KeyPath] that specify which animation element | ||
* the property resides on. | ||
* @param callback A callback that will be invoked during the drawing pass with current frame info. The frame | ||
* info can be used to alter the property's value based on the original animation data or it | ||
* can be completely ignored and an arbitrary value can be returned. In this case, you may want | ||
* the overloaded version of this function that takes a static value instead of a callback. | ||
*/ | ||
@Composable | ||
fun <T> rememberLottieDynamicProperty( | ||
property: T, | ||
vararg keyPath: String, | ||
callback: (frameInfo: LottieFrameInfo<T>) -> T, | ||
): LottieDynamicProperty<T> { | ||
val keyPathObj = remember(keyPath) { KeyPath(*keyPath) } | ||
val callbackState by rememberUpdatedState(callback) | ||
return remember(keyPathObj, property) { | ||
LottieDynamicProperty( | ||
property, | ||
keyPathObj, | ||
) { callbackState(it) } | ||
} | ||
} | ||
|
||
/** | ||
* @see rememberLottieDynamicProperty | ||
*/ | ||
class LottieDynamicProperty<T> internal constructor( | ||
internal val property: T, | ||
internal val keyPath: KeyPath, | ||
internal val callback: (frameInfo: LottieFrameInfo<T>) -> T, | ||
) { | ||
constructor(property: T, keyPath: KeyPath, value: T) : this(property, keyPath, { value }) | ||
} | ||
|
||
/** | ||
* @see rememberLottieDynamicProperties | ||
*/ | ||
class LottieDynamicProperties internal constructor( | ||
private val intProperties: List<LottieDynamicProperty<Int>>, | ||
private val pointFProperties: List<LottieDynamicProperty<PointF>>, | ||
private val floatProperties: List<LottieDynamicProperty<Float>>, | ||
private val scaleProperties: List<LottieDynamicProperty<ScaleXY>>, | ||
private val colorFilterProperties: List<LottieDynamicProperty<ColorFilter>>, | ||
private val intArrayProperties: List<LottieDynamicProperty<IntArray>>, | ||
) { | ||
@Suppress("UNCHECKED_CAST") | ||
constructor(properties: List<LottieDynamicProperty<*>>) : this( | ||
properties.filter { it.property is Int } as List<LottieDynamicProperty<Int>>, | ||
properties.filter { it.property is PointF } as List<LottieDynamicProperty<PointF>>, | ||
properties.filter { it.property is Float } as List<LottieDynamicProperty<Float>>, | ||
properties.filter { it.property is ScaleXY } as List<LottieDynamicProperty<ScaleXY>>, | ||
properties.filter { it.property is ColorFilter } as List<LottieDynamicProperty<ColorFilter>>, | ||
properties.filter { it.property is IntArray } as List<LottieDynamicProperty<IntArray>>, | ||
) | ||
|
||
internal fun addTo(drawable: LottieDrawable) { | ||
intProperties.forEach { p -> | ||
drawable.addValueCallback(p.keyPath, p.property, p.callback.toValueCallback()) | ||
} | ||
pointFProperties.forEach { p -> | ||
drawable.addValueCallback(p.keyPath, p.property, p.callback.toValueCallback()) | ||
} | ||
floatProperties.forEach { p -> | ||
drawable.addValueCallback(p.keyPath, p.property, p.callback.toValueCallback()) | ||
} | ||
scaleProperties.forEach { p -> | ||
drawable.addValueCallback(p.keyPath, p.property, p.callback.toValueCallback()) | ||
} | ||
colorFilterProperties.forEach { p -> | ||
drawable.addValueCallback(p.keyPath, p.property, p.callback.toValueCallback()) | ||
} | ||
intArrayProperties.forEach { p -> | ||
drawable.addValueCallback(p.keyPath, p.property, p.callback.toValueCallback()) | ||
} | ||
} | ||
|
||
internal fun removeFrom(drawable: LottieDrawable) { | ||
intProperties.forEach { p -> | ||
drawable.addValueCallback(p.keyPath, p.property, null as LottieValueCallback<Int>?) | ||
} | ||
pointFProperties.forEach { p -> | ||
drawable.addValueCallback(p.keyPath, p.property, null as LottieValueCallback<PointF>?) | ||
} | ||
floatProperties.forEach { p -> | ||
drawable.addValueCallback(p.keyPath, p.property, null as LottieValueCallback<Float>?) | ||
} | ||
scaleProperties.forEach { p -> | ||
drawable.addValueCallback(p.keyPath, p.property, null as LottieValueCallback<ScaleXY>?) | ||
} | ||
colorFilterProperties.forEach { p -> | ||
drawable.addValueCallback(p.keyPath, p.property, null as LottieValueCallback<ColorFilter>?) | ||
} | ||
intArrayProperties.forEach { p -> | ||
drawable.addValueCallback(p.keyPath, p.property, null as LottieValueCallback<IntArray>?) | ||
} | ||
} | ||
} | ||
|
||
private fun <T> ((frameInfo: LottieFrameInfo<T>) -> T).toValueCallback() = object : LottieValueCallback<T>() { | ||
override fun getValue(frameInfo: LottieFrameInfo<T>): T { | ||
return invoke(frameInfo) | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.