Added a coroutines sample
diff --git a/lottie-compose/src/main/java/com/airbnb/lottie/compose/animateLottieCompositionSuspend.kt b/lottie-compose/src/main/java/com/airbnb/lottie/compose/animateLottieCompositionSuspend.kt
index aedf9e1..6c9268d 100644
--- a/lottie-compose/src/main/java/com/airbnb/lottie/compose/animateLottieCompositionSuspend.kt
+++ b/lottie-compose/src/main/java/com/airbnb/lottie/compose/animateLottieCompositionSuspend.kt
@@ -49,27 +49,31 @@
clipSpec: LottieClipSpec? = null,
cancellationBehavior: LottieCancellationBehavior = LottieCancellationBehavior.Immediate,
speed: Float = 1f,
-) {
+ snapToMinProgress: Boolean = true,
+ lastFrameTimeNanos: Long? = null,
+): Long? {
require(speed != 0f) { "Speed must not be 0" }
require(speed.isFinite()) { "Speed must be a finite number. It is $speed." }
- composition ?: return
+ composition ?: return null
val context = when (cancellationBehavior) {
LottieCancellationBehavior.Immediate -> EmptyCoroutineContext
LottieCancellationBehavior.AtEnd -> NonCancellable
}
- withContext(context) {
+ return withContext(context) {
val minProgress = clipSpec?.getMinProgress(composition) ?: 0f
val maxProgress = clipSpec?.getMaxProgress(composition) ?: 1f
progress.value = when {
+ !snapToMinProgress -> progress.value.coerceIn(minProgress, maxProgress)
speed >= 0 -> minProgress
else -> maxProgress
}
- var lastFrameTime = withFrameNanos { it }
+ @Suppress("LocalVariableName")
+ var _lastFrameTimeNanos = lastFrameTimeNanos ?: withFrameNanos { it }
var done = false
while (!done) {
withFrameNanos { frameTime ->
- val dTime = (frameTime - lastFrameTime) / TimeUnit.MILLISECONDS.toNanos(1).toFloat()
- lastFrameTime = frameTime
+ val dTime = (frameTime - _lastFrameTimeNanos) / TimeUnit.MILLISECONDS.toNanos(1).toFloat()
+ _lastFrameTimeNanos = frameTime
val dProgress = (dTime * speed) / composition.duration
val rawProgress = minProgress + ((progress.value - minProgress) + dProgress)
progress.value = rawProgress.coerceIn(minProgress, maxProgress)
@@ -79,5 +83,6 @@
}
}
}
+ _lastFrameTimeNanos
}
}
\ No newline at end of file
diff --git a/sample-compose/src/main/java/com/airbnb/lottie/sample/compose/ComposeActivity.kt b/sample-compose/src/main/java/com/airbnb/lottie/sample/compose/ComposeActivity.kt
index 2665ded..470c328 100644
--- a/sample-compose/src/main/java/com/airbnb/lottie/sample/compose/ComposeActivity.kt
+++ b/sample-compose/src/main/java/com/airbnb/lottie/sample/compose/ComposeActivity.kt
@@ -80,6 +80,7 @@
composable(Route.LottieFiles.route) { LottieFilesPage(navController) }
composable(Route.Examples.route) { ExamplesPage(navController) }
composable(Route.BasicUsageExamples.route) { BasicUsageExamplesPage() }
+ composable(Route.CoroutinesExamples.route) { CoroutinesExamplesPage() }
composable(Route.TransitionsExamples.route) { TransitionsExamplesPage() }
composable(Route.ViewPagerExample.route) { ViewPagerExamplePage() }
composable(Route.NetworkExamples.route) { NetworkExamplesPage() }
diff --git a/sample-compose/src/main/java/com/airbnb/lottie/sample/compose/Route.kt b/sample-compose/src/main/java/com/airbnb/lottie/sample/compose/Route.kt
index 93b4770..232d780 100644
--- a/sample-compose/src/main/java/com/airbnb/lottie/sample/compose/Route.kt
+++ b/sample-compose/src/main/java/com/airbnb/lottie/sample/compose/Route.kt
@@ -18,6 +18,8 @@
object BasicUsageExamples : Route("basic usage examples")
+ object CoroutinesExamples : Route("coroutines examples")
+
object TransitionsExamples : Route("transitions examples")
object ViewPagerExample : Route("view pager example")
diff --git a/sample-compose/src/main/java/com/airbnb/lottie/sample/compose/examples/CoroutinesExamplesPage.kt b/sample-compose/src/main/java/com/airbnb/lottie/sample/compose/examples/CoroutinesExamplesPage.kt
new file mode 100644
index 0000000..679b8d5
--- /dev/null
+++ b/sample-compose/src/main/java/com/airbnb/lottie/sample/compose/examples/CoroutinesExamplesPage.kt
@@ -0,0 +1,113 @@
+package com.airbnb.lottie.sample.compose.examples
+
+import androidx.compose.foundation.clickable
+import androidx.compose.foundation.layout.*
+import androidx.compose.foundation.rememberScrollState
+import androidx.compose.foundation.verticalScroll
+import androidx.compose.runtime.*
+import androidx.compose.ui.Modifier
+import androidx.compose.ui.unit.dp
+import com.airbnb.lottie.compose.*
+import com.airbnb.lottie.sample.compose.R
+import kotlinx.coroutines.isActive
+
+@Composable
+fun CoroutinesExamplesPage() {
+ UsageExamplePageScaffold {
+ Column(
+ modifier = Modifier
+ .fillMaxWidth()
+ .verticalScroll(rememberScrollState())
+ ) {
+ Box(modifier = Modifier.height(16.dp))
+ ExampleCard("Example 1", "Repeat once") {
+ Example1()
+ }
+ ExampleCard("Example 2", "Repeat once. Click to repeat again") {
+ Example2()
+ }
+ ExampleCard("Example 3", "Repeat forever") {
+ Example3()
+ }
+ ExampleCard("Example 4", "Click to toggle playback") {
+ Example4()
+ }
+ }
+ }
+}
+
+
+@Composable
+private fun Example1() {
+ val composition by lottieComposition(LottieCompositionSpec.RawRes(R.raw.heart))
+ val progress = remember { mutableStateOf(0f) }
+
+ LaunchedEffect(composition) {
+ animateLottieComposition(composition, progress)
+ }
+ LottieAnimation(composition, progress.value)
+}
+
+@Composable
+private fun Example2() {
+ var iteration by remember { mutableStateOf(1) }
+ val composition by lottieComposition(LottieCompositionSpec.RawRes(R.raw.heart))
+ val progress = remember { mutableStateOf(0f) }
+
+ LaunchedEffect(composition, iteration) {
+ animateLottieComposition(composition, progress)
+ }
+ LottieAnimation(
+ composition,
+ progress.value,
+ modifier = Modifier
+ .clickable { iteration++ }
+ )
+}
+
+@Composable
+private fun Example3() {
+ val composition by lottieComposition(LottieCompositionSpec.RawRes(R.raw.heart))
+ val progress = remember { mutableStateOf(0f) }
+
+ LaunchedEffect(composition) {
+ // Return early when the composition is parsing so it doesn't loop in animateLottieComposition
+ // over and over again.
+ composition ?: return@LaunchedEffect
+ var lastFrameNanos: Long? = null
+ while (isActive) {
+ lastFrameNanos = animateLottieComposition(
+ composition,
+ progress,
+ lastFrameTimeNanos = lastFrameNanos,
+ )
+ }
+ }
+ LottieAnimation(composition, progress.value)
+}
+
+@Composable
+private fun Example4() {
+ var isPlaying by remember { mutableStateOf(true) }
+ val composition by lottieComposition(LottieCompositionSpec.RawRes(R.raw.heart))
+ val progress = remember { mutableStateOf(0f) }
+
+ LaunchedEffect(composition, isPlaying) {
+ if (composition == null || !isPlaying) return@LaunchedEffect
+ var snapToMinProgress = false
+ while (isActive) {
+ animateLottieComposition(
+ composition,
+ progress,
+ snapToMinProgress = snapToMinProgress,
+ )
+ snapToMinProgress = true
+ }
+ }
+ LottieAnimation(
+ composition,
+ progress.value,
+ modifier = Modifier
+ .clickable { isPlaying = !isPlaying }
+ )
+}
\ No newline at end of file
diff --git a/sample-compose/src/main/java/com/airbnb/lottie/sample/compose/examples/ExamplesPage.kt b/sample-compose/src/main/java/com/airbnb/lottie/sample/compose/examples/ExamplesPage.kt
index e27ea89..fac0c3d 100644
--- a/sample-compose/src/main/java/com/airbnb/lottie/sample/compose/examples/ExamplesPage.kt
+++ b/sample-compose/src/main/java/com/airbnb/lottie/sample/compose/examples/ExamplesPage.kt
@@ -24,13 +24,19 @@
Marquee(stringResource(R.string.examples_title))
ListItem(
text = { Text("Basic Usage") },
- secondaryText = { Text("Various example of simple Lottie usage.") },
+ secondaryText = { Text("Various example of simple Lottie usage") },
modifier = Modifier
.clickable { navController.navigate(Route.BasicUsageExamples) }
)
ListItem(
+ text = { Text("Coroutines Usage") },
+ secondaryText = { Text("Animating lottie with a suspending coroutine") },
+ modifier = Modifier
+ .clickable { navController.navigate(Route.CoroutinesExamples) }
+ )
+ ListItem(
text = { Text("Transitions") },
- secondaryText = { Text("Sequencing segments of an animation based on state.") },
+ secondaryText = { Text("Sequencing segments of an animation based on state") },
modifier = Modifier
.clickable { navController.navigate(Route.TransitionsExamples) }
)