Farhazul Mullick
12/11/2023, 11:45 AMHristijan
12/11/2023, 12:38 PMFarhazul Mullick
12/11/2023, 3:13 PMAlejandro Rios
12/11/2023, 4:36 PMFarhazul Mullick
12/12/2023, 3:54 PM@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