blob: f79666d2f59c40a95b397838bae79b4bf73fb6a0 [file] [log] [blame]
package com.airbnb.lottie.sample.compose.composables
import android.util.Log
import androidx.annotation.RawRes
import androidx.compose.foundation.Canvas
import androidx.compose.runtime.*
import androidx.compose.runtime.dispatch.withFrameMillis
import androidx.compose.ui.Modifier
import androidx.compose.ui.geometry.Size
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.graphics.Matrix
import androidx.compose.ui.graphics.Paint
import androidx.compose.ui.graphics.drawscope.drawCanvas
import androidx.compose.ui.graphics.drawscope.withTransform
import androidx.compose.ui.graphics.nativeCanvas
import androidx.compose.ui.platform.ContextAmbient
import androidx.compose.ui.platform.LifecycleOwnerAmbient
import androidx.compose.ui.unit.dp
import androidx.lifecycle.whenStarted
import com.airbnb.lottie.LottieComposition
import com.airbnb.lottie.LottieCompositionFactory
import com.airbnb.lottie.LottieDrawable
sealed class LottieAnimationSpec {
class RawRes(@androidx.annotation.RawRes val resId: Int) : LottieAnimationSpec()
class Url(val url: String) : LottieAnimationSpec()
}
@Composable
fun LottieAnimation(
spec: LottieAnimationSpec,
modifier: Modifier = Modifier
) {
val context = ContextAmbient.current
var composition: LottieComposition? by remember { mutableStateOf(null) }
onCommit(spec) {
var isDisposed = false
val task = when(spec) {
is LottieAnimationSpec.RawRes -> LottieCompositionFactory.fromRawRes(context, spec.resId)
is LottieAnimationSpec.Url -> LottieCompositionFactory.fromUrl(context, spec.url)
}
task.addListener { c ->
if (!isDisposed) composition = c
}.addFailureListener { e ->
Log.d("Gabe", "Animation failed to load", e)
}
onDispose {
isDisposed = true
}
}
LottieAnimation(composition, modifier)
}
@Composable
fun LottieAnimation(
composition: LottieComposition?,
modifier: Modifier = Modifier
) {
val drawable = remember { LottieDrawable() }
val animationTime = animationTimeMillis()
drawable.progress = (animationTime.value / (drawable.composition?.duration ?: 1f)) % 1f
onCommit(composition) {
drawable.composition = composition
}
if (composition == null || composition.duration == 0f) return
drawable.progress = (animationTime.value / (composition.duration)) % 1f
Canvas(modifier = modifier) {
drawCanvas { canvas, size ->
withTransform({
scale(size.width / composition.bounds.width().toFloat(), size.height / composition.bounds.height().toFloat(), 0f, 0f)
}) {
drawable.draw(canvas.nativeCanvas)
}
}
}
}
@Composable
private fun animationTimeMillis(): State<Long> {
val millisState = remember { mutableStateOf(0L) }
val lifecycleOwner = LifecycleOwnerAmbient.current
launchInComposition {
val startTime = withFrameMillis { it }
lifecycleOwner.whenStarted {
while (true) {
withFrameMillis { frameTime ->
millisState.value = frameTime - startTime
}
}
}
}
return millisState
}