Skip to content

Commit 7557707

Browse files
authored
Refactor Lottie animation scaling using drawWithContent modifier (#2801)
- Pull up aspect-ratio scaling and centering from canvas operations to top-level LottieAnimation via RemoteModifier.drawWithContent - Remove the now-duplicated scaling from RenderShapes, so the scale is applied exactly once Rendering is unchanged: all 33 Roborazzi goldens verify unmodified, including the 16 LottieScalingDiffScreenshotTest aspect-ratio cases.
1 parent 3960cf3 commit 7557707

2 files changed

Lines changed: 39 additions & 36 deletions

File tree

remotecompose/lottie/src/main/java/com/google/android/horologist/remotecompose/lottie/LottieAnimation.kt

Lines changed: 22 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -23,8 +23,10 @@ import androidx.compose.remote.creation.compose.layout.RemoteAlignment
2323
import androidx.compose.remote.creation.compose.layout.RemoteBox
2424
import androidx.compose.remote.creation.compose.layout.RemoteComposable
2525
import androidx.compose.remote.creation.compose.modifier.RemoteModifier
26+
import androidx.compose.remote.creation.compose.modifier.drawWithContent
2627
import androidx.compose.remote.creation.compose.state.RemoteFloat
2728
import androidx.compose.remote.creation.compose.state.floor
29+
import androidx.compose.remote.creation.compose.state.min
2830
import androidx.compose.remote.creation.compose.state.rf
2931
import androidx.compose.runtime.Composable
3032
import androidx.compose.runtime.CompositionLocalProvider
@@ -139,8 +141,27 @@ internal fun LottieAnimation(
139141
.filter { l -> l.index != null && l.transform != null }
140142
.associate { l -> Pair(l.index!!, l.transform!!) }
141143

144+
val lottieWidth = animation.width.rf
145+
val lottieHeight = animation.height.rf
146+
147+
val scaleModifier = RemoteModifier.drawWithContent {
148+
val canvasWidth = size.width
149+
val canvasHeight = size.height
150+
151+
val scaleX = canvasWidth / lottieWidth
152+
val scaleY = canvasHeight / lottieHeight
153+
val scale = min(scaleX, scaleY)
154+
155+
val scaledWidth = lottieWidth * scale
156+
val scaledHeight = lottieHeight * scale
157+
val dx = (canvasWidth - scaledWidth) / 2.rf
158+
val dy = (canvasHeight - scaledHeight) / 2.rf
159+
160+
translate(dx, dy) { scale(scale, scale) { drawContent() } }
161+
}
162+
142163
RemoteBox(
143-
modifier = modifier,
164+
modifier = modifier.then(scaleModifier),
144165
// TODO: 496943072 - ANDROID_NATIVE player doesn't support clipping yet, so we need to avoid
145166
// clipping for now until it does. coming in cl/893506559
146167
// .clip(RemoteRectangleShape)

remotecompose/lottie/src/main/java/com/google/android/horologist/remotecompose/lottie/renderer/Shape.kt

Lines changed: 17 additions & 35 deletions
Original file line numberDiff line numberDiff line change
@@ -22,8 +22,6 @@ import androidx.compose.remote.creation.compose.layout.RemoteCanvas
2222
import androidx.compose.remote.creation.compose.layout.RemoteComposable
2323
import androidx.compose.remote.creation.compose.modifier.RemoteModifier
2424
import androidx.compose.remote.creation.compose.modifier.fillMaxSize
25-
import androidx.compose.remote.creation.compose.state.min
26-
import androidx.compose.remote.creation.compose.state.rf
2725
import androidx.compose.runtime.Composable
2826
import com.google.android.horologist.remotecompose.lottie.LocalAnimationSettings
2927
import com.google.android.horologist.remotecompose.lottie.LottieSettings
@@ -54,42 +52,26 @@ internal fun RenderShapes(shapes: List<GraphicElement>, transformStack: List<Tra
5452
val animationSettings = LocalAnimationSettings.current
5553
val shapeGroups = gatherShapes(shapes, animationSettings)
5654

55+
// Aspect-ratio scaling and centering is applied once, at the top level, by the
56+
// drawWithContent modifier in LottieAnimation - shapes draw in raw Lottie coordinates here.
5757
RemoteCanvas(modifier = RemoteModifier.fillMaxSize()) {
58-
val canvasWidth = remote.component.width
59-
val canvasHeight = remote.component.height
60-
val lottieWidth = animationSettings.width.rf
61-
val lottieHeight = animationSettings.height.rf
62-
63-
val scaleX = canvasWidth / lottieWidth
64-
val scaleY = canvasHeight / lottieHeight
65-
val scale = min(scaleX, scaleY)
66-
67-
val scaledWidth = lottieWidth * scale
68-
val scaledHeight = lottieHeight * scale
69-
val dx = (canvasWidth - scaledWidth) / 2.rf
70-
val dy = (canvasHeight - scaledHeight) / 2.rf
71-
72-
translate(dx, dy) {
73-
scale(scale) {
74-
for (shapeGroup in shapeGroups) {
75-
val paint = shapeGroup.style.getPaint()
76-
77-
for (transform in transformStack) {
78-
remoteCanvas.save()
79-
transform(transform, paint, animationSettings, remoteCanvas)
80-
}
81-
82-
usePaint(paint) {
83-
for (shape in shapeGroup.shapes) {
84-
shape.draw(this, remoteCanvas)
85-
}
86-
}
87-
88-
for (transform in transformStack) {
89-
remoteCanvas.restore()
90-
}
58+
for (shapeGroup in shapeGroups) {
59+
val paint = shapeGroup.style.getPaint()
60+
61+
for (transform in transformStack) {
62+
remoteCanvas.save()
63+
transform(transform, paint, animationSettings, remoteCanvas)
64+
}
65+
66+
usePaint(paint) {
67+
for (shape in shapeGroup.shapes) {
68+
shape.draw(this, remoteCanvas)
9169
}
9270
}
71+
72+
for (transform in transformStack) {
73+
remoteCanvas.restore()
74+
}
9375
}
9476
}
9577
}

0 commit comments

Comments
 (0)