https://kotlinlang.org logo
#compose-ios
Title
# compose-ios
f

Farhazul Mullick

12/11/2023, 11:45 AM
Can we use lottie animations in compose KMM.
f

Farhazul Mullick

12/11/2023, 3:13 PM
Thanks, really helped me to craft animations working on both platforms.
a

Alejandro Rios

12/11/2023, 4:36 PM
Can you explain how?
f

Farhazul Mullick

12/12/2023, 3:54 PM
You would have to create actual/expect methods for the same. Handle animations in android with LottieAnimations from airbnb and in iOS you can use the code below. You can suggest further performance improvements.
Copy code
@Composable
expect fun LottieAnimation(
    modifier: Modifier = Modifier,
    animFile: FileResource,
    iterations: Int = Int.MAX_VALUE
)

@Composable
actual fun LottieAnimation(
    modifier: Modifier,
    animFile: FileResource,
    iterations: Int
) {
    var animation by remember { mutableStateOf<Animation?>(null) }

    LaunchedEffect(Unit) {
         // .value returns emptyString if readText() returns null.
        val byteArray: ByteArray = animFile.readText().value.encodeToByteArray()
        animation = Animation.makeFromData(Data.makeFromBytes(bytes = byteArray))
    }

    when (val value = animation) {
        null -> return
        else -> Box(modifier = modifier) {
            SkiaAnimation(
                modifier = Modifier.fillMaxSize(),
                animation = value,
                iterations
            )
        }
    }
}

@Composable
private fun SkiaAnimation(
    modifier: Modifier = Modifier,
    animation: Animation,
    iterations: Int
) {
    var elapsedSeconds: Float by remember { mutableStateOf(DefaultElapsedSeconds) }
    var lastTimeInMillis: Long by remember { mutableStateOf(DefaultLastTimeInMillis) }
    val invalidationController = remember { InvalidationController() }
    var itr : Int by remember { mutableStateOf(iterations) }


    Surface(
        modifier = modifier
            .drawAnimationOnCanvas(
                animation = animation,
                time = elapsedSeconds,
                invalidationController = invalidationController
            )
    ) {}


    LaunchedEffect(Unit) {
        do {
            withFrameMillis { absoluteMillis ->
                if (lastTimeInMillis > DefaultLastTimeInMillis) {
                    val deltaMillis =
                        (absoluteMillis - lastTimeInMillis)
                    elapsedSeconds += deltaMillis / MillisecondsInOneSecond
                }
                lastTimeInMillis = absoluteMillis
            }
            if (elapsedSeconds >= animation.duration) {
                if (itr > 1) {
                    elapsedSeconds %= animation.duration
                    itr--
                }
                else {
                    this.coroutineContext.job.cancel()
                    return@LaunchedEffect
                }
            }
        } while (this.coroutineContext.job.isActive)
    }
}

private fun Modifier.drawAnimationOnCanvas(
    animation: Animation,
    time: Float,
    invalidationController: InvalidationController
): Modifier = this then drawWithContent {
    drawIntoCanvas { canvas ->
        animation.seekFrameTime(time, invalidationController)
        animation.render(
            canvas = canvas.nativeCanvas,
            dst = Rect.makeWH(size.width, size.height),
        )
    }
}

private const val MillisecondsInOneSecond = 1000F
private const val DefaultElapsedSeconds = 0f
private const val DefaultLastTimeInMillis = 0L
gratitude thank you 2
4 Views