blob: 653311ee36cc267f1f4899e9d20ad305c905bf9f [file] [log] [blame]
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)
}
}