Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Fix LottieAnimation recomposes on every frame degrading performance #2078

Merged
merged 2 commits into from May 24, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
Expand Up @@ -22,6 +22,6 @@ class ComposeIssueReproActivity : AppCompatActivity() {
fun Content() {
val composition by rememberLottieComposition(LottieCompositionSpec.RawRes(R.raw.heart))
val progress by animateLottieCompositionAsState(composition)
LottieAnimation(composition, progress)
LottieAnimation(composition, { progress })
}
}
Expand Up @@ -32,12 +32,12 @@ import kotlin.math.roundToInt
*
* @param composition The composition that will be rendered. To generate a [LottieComposition], you can use
* [rememberLottieComposition].
* @param progress The progress (between 0 and 1) that should be rendered. If you want to render a specific
* frame, you can use [LottieComposition.getFrameForProgress]. In most cases, you will want
* to use one of the overloaded LottieAnimation composables that drives the animation for you.
* The overloads that have isPlaying as a parameter instead of progress will drive the
* animation automatically. You may want to use this version if you want to drive the animation
* from your own Animatable or via events such as download progress or a gesture.
* @param progressProvider A provider for the progress (between 0 and 1) that should be rendered. If you want to render a
* specific frame, you can use [LottieComposition.getFrameForProgress]. In most cases, you will want
* to use one of the overloaded LottieAnimation composables that drives the animation for you.
* The overloads that have isPlaying as a parameter instead of progress will drive the
* animation automatically. You may want to use this version if you want to drive the animation
* from your own Animatable or via events such as download progress or a gesture.
* @param outlineMasksAndMattes Enable this to debug slow animations by outlining masks and mattes.
* The performance overhead of the masks and mattes will be proportional to the
* surface area of all of the masks/mattes combined.
Expand Down Expand Up @@ -69,7 +69,7 @@ import kotlin.math.roundToInt
@Composable
fun LottieAnimation(
composition: LottieComposition?,
@FloatRange(from = 0.0, to = 1.0) progress: Float,
@FloatRange(from = 0.0, to = 1.0) progressProvider: () -> Float,
modifier: Modifier = Modifier,
outlineMasksAndMattes: Boolean = false,
applyOpacityToLayers: Boolean = false,
Expand Down Expand Up @@ -114,16 +114,52 @@ fun LottieAnimation(
drawable.isApplyingOpacityToLayersEnabled = applyOpacityToLayers
drawable.maintainOriginalImageBounds = maintainOriginalImageBounds
drawable.clipToCompositionBounds = clipToCompositionBounds
drawable.progress = progress
drawable.progress = progressProvider()
drawable.setBounds(0, 0, composition.bounds.width(), composition.bounds.height())
drawable.draw(canvas.nativeCanvas, matrix)
}
}
}

/**
* This is like [LottieAnimation] except that it takes a raw progress parameter instead of taking a progress provider.
*
* @see LottieAnimation
*/
@Composable
fun LottieAnimation(
composition: LottieComposition?,
@FloatRange(from = 0.0, to = 1.0) progress: Float,
modifier: Modifier = Modifier,
outlineMasksAndMattes: Boolean = false,
applyOpacityToLayers: Boolean = false,
enableMergePaths: Boolean = false,
renderMode: RenderMode = RenderMode.AUTOMATIC,
maintainOriginalImageBounds: Boolean = false,
dynamicProperties: LottieDynamicProperties? = null,
alignment: Alignment = Alignment.Center,
contentScale: ContentScale = ContentScale.Fit,
clipToCompositionBounds: Boolean = true,
) {
LottieAnimation(
composition,
{ progress },
modifier,
outlineMasksAndMattes,
applyOpacityToLayers,
enableMergePaths,
renderMode,
maintainOriginalImageBounds,
dynamicProperties,
alignment,
contentScale,
clipToCompositionBounds,
)
}

/**
* This is like [LottieAnimation] except that it handles driving the animation via [animateLottieCompositionAsState]
* instead of taking a raw progress parameter.
* instead of taking a progress provider.
*
* @see LottieAnimation
* @see animateLottieCompositionAsState
Expand Down Expand Up @@ -157,7 +193,7 @@ fun LottieAnimation(
)
LottieAnimation(
composition,
progress,
{ progress },
modifier,
outlineMasksAndMattes,
applyOpacityToLayers,
Expand Down
Expand Up @@ -64,7 +64,7 @@ private fun Example1() {
iterations = LottieConstants.IterateForever,
)
}
LottieAnimation(anim.composition, anim.progress)
LottieAnimation(anim.composition, { anim.progress })
}

@Composable
Expand All @@ -84,7 +84,7 @@ private fun Example2() {
}
}
Box {
LottieAnimation(anim.composition, anim.progress)
LottieAnimation(anim.composition, { anim.progress })
Slider(
value = sliderGestureProgress ?: anim.progress,
onValueChange = { sliderGestureProgress = it },
Expand All @@ -110,7 +110,7 @@ private fun Example3() {
)
}
Box {
LottieAnimation(composition, anim.progress)
LottieAnimation(composition, { anim.progress })
Slider(
value = speed,
onValueChange = { speed = it },
Expand Down Expand Up @@ -144,7 +144,7 @@ private fun Example4() {
}
LottieAnimation(
composition,
animatable.progress,
{ animatable.progress },
modifier = Modifier
.clickable { nonce++ }
)
Expand All @@ -162,7 +162,7 @@ private fun Example5() {
}
LottieAnimation(
composition,
animatable.progress,
{ animatable.progress },
modifier = Modifier
.clickable { shouldPlay = !shouldPlay }
)
Expand Down
Expand Up @@ -140,7 +140,7 @@ private fun Example6() {
)
LottieAnimation(
composition,
progress,
{ progress },
)
}

Expand Down
Expand Up @@ -90,7 +90,7 @@ fun SingleCompositionTransition(section: TransitionSection) {
} while (s == TransitionSection.LoopMiddle)
}
}
LottieAnimation(composition, animatable.progress)
LottieAnimation(composition, { animatable.progress })
}

@Composable
Expand All @@ -113,5 +113,5 @@ fun SplitCompositionTransition(section: TransitionSection) {
)
}

LottieAnimation(animatable.composition, animatable.progress)
LottieAnimation(animatable.composition, { animatable.progress })
}
Expand Up @@ -60,7 +60,7 @@ private fun WalkthroughAnimation(pagerState: PagerState) {
val progress by derivedStateOf { (pagerState.currentPage + pagerState.currentPageOffset) / (pagerState.pageCount - 1f) }
LottieAnimation(
composition,
progress,
{ progress },
modifier = Modifier
.fillMaxSize()
)
Expand Down
Expand Up @@ -255,7 +255,7 @@ fun PlayerPageContent(
) {
PlayerPageLottieAnimation(
composition,
state.animatable.progress,
{ state.animatable.progress },
modifier = Modifier
// TODO: figure out how maxWidth can play nice with the aspectRatio modifier inside of LottieAnimation.
.fillMaxWidth()
Expand Down Expand Up @@ -291,12 +291,12 @@ fun PlayerPageContent(
@Composable
private fun PlayerPageLottieAnimation(
composition: LottieComposition?,
progress: Float,
progressProvider: () -> Float,
modifier: Modifier = Modifier,
) {
LottieAnimation(
composition,
progress,
progressProvider,
modifier = modifier,
)
}
Expand Down