Diagonal square works
diff --git a/issue-repro/src/main/java/com/airbnb/lottie/issues/IssueReproActivity.kt b/issue-repro/src/main/java/com/airbnb/lottie/issues/IssueReproActivity.kt
index 7fc7b8a..80f04ca 100755
--- a/issue-repro/src/main/java/com/airbnb/lottie/issues/IssueReproActivity.kt
+++ b/issue-repro/src/main/java/com/airbnb/lottie/issues/IssueReproActivity.kt
@@ -2,8 +2,17 @@
import android.os.Bundle
import androidx.activity.compose.setContent
-import androidx.annotation.FloatRange
import androidx.appcompat.app.AppCompatActivity
+import androidx.compose.foundation.background
+import androidx.compose.foundation.clickable
+import androidx.compose.foundation.layout.Arrangement
+import androidx.compose.foundation.layout.Column
+import androidx.compose.foundation.layout.fillMaxSize
+import androidx.compose.foundation.layout.size
+import androidx.compose.ui.Alignment
+import androidx.compose.ui.Modifier
+import androidx.compose.ui.graphics.Color
+import androidx.compose.ui.unit.dp
import com.airbnb.lottie.compose.LottieAnimationSpec
import com.airbnb.lottie.compose.LottieAnimationState
import com.airbnb.lottie.compose.rememberLottieComposition
@@ -19,10 +28,19 @@
isPlaying = true,
repeatCount = Integer.MAX_VALUE,
)
- ComposeLottieAnimation(
- compositionResult,
- state,
- )
+ Column(
+ verticalArrangement = Arrangement.Center,
+ horizontalAlignment = Alignment.CenterHorizontally,
+ modifier = Modifier.fillMaxSize(),
+ ) {
+ ComposeLottieAnimation(
+ compositionResult,
+ state,
+ modifier = Modifier
+ .size(256.dp)
+ .background(Color.Blue)
+ )
+ }
}
}
}
diff --git a/issue-repro/src/main/res/raw/anim.json b/issue-repro/src/main/res/raw/anim.json
index 072ccdb..8ac014b 100644
--- a/issue-repro/src/main/res/raw/anim.json
+++ b/issue-repro/src/main/res/raw/anim.json
@@ -1 +1 @@
-{"v":"5.7.4","fr":60,"ip":0,"op":61,"w":400,"h":400,"nm":"Test","ddd":0,"assets":[],"layers":[{"ddd":0,"ind":1,"ty":4,"nm":"Shape Layer 1","sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[200,200,0],"ix":2,"l":2},"a":{"a":0,"k":[0,0,0],"ix":1,"l":2},"s":{"a":0,"k":[100,100,100],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"ty":"rc","d":1,"s":{"a":0,"k":[150,150],"ix":2},"p":{"a":0,"k":[0,0],"ix":3},"r":{"a":0,"k":0,"ix":4},"nm":"Rectangle Path 1","mn":"ADBE Vector Shape - Rect","hd":false},{"ty":"fl","c":{"a":0,"k":[1,0,0,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":1,"k":[{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"t":0,"s":[-125,-125],"to":[41.667,41.667],"ti":[-41.667,-41.667]},{"t":60,"s":[125,125]}],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Rectangle 1","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":0,"op":61,"st":0,"bm":0}],"markers":[]}
\ No newline at end of file
+{"v":"5.7.4","fr":60,"ip":0,"op":181,"w":400,"h":400,"nm":"Diagonal","ddd":0,"assets":[],"layers":[{"ddd":0,"ind":1,"ty":4,"nm":"Shape Layer 1","sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[200,200,0],"ix":2,"l":2},"a":{"a":0,"k":[0,0,0],"ix":1,"l":2},"s":{"a":0,"k":[100,100,100],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"ty":"rc","d":1,"s":{"a":0,"k":[150,150],"ix":2},"p":{"a":0,"k":[0,0],"ix":3},"r":{"a":0,"k":0,"ix":4},"nm":"Rectangle Path 1","mn":"ADBE Vector Shape - Rect","hd":false},{"ty":"fl","c":{"a":0,"k":[1,0,0,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":1,"k":[{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"t":0,"s":[-125,-125],"to":[41.667,41.667],"ti":[-41.667,-41.667]},{"t":180,"s":[125,125]}],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Rectangle 1","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":0,"op":181,"st":0,"bm":0}],"markers":[]}
\ No newline at end of file
diff --git a/lottie-compose/src/main/java/com/airbnb/lottie/compose/renderer/LayerTransform.kt b/lottie-compose/src/main/java/com/airbnb/lottie/compose/renderer/ComposeLottieTransform.kt
similarity index 70%
rename from lottie-compose/src/main/java/com/airbnb/lottie/compose/renderer/LayerTransform.kt
rename to lottie-compose/src/main/java/com/airbnb/lottie/compose/renderer/ComposeLottieTransform.kt
index 4974626..ece86b3 100644
--- a/lottie-compose/src/main/java/com/airbnb/lottie/compose/renderer/LayerTransform.kt
+++ b/lottie-compose/src/main/java/com/airbnb/lottie/compose/renderer/ComposeLottieTransform.kt
@@ -3,14 +3,14 @@
import android.graphics.PointF
import androidx.compose.runtime.*
import androidx.compose.ui.Modifier
-import androidx.compose.ui.composed
import androidx.compose.ui.graphics.graphicsLayer
import com.airbnb.lottie.model.animatable.AnimatableTransform
+import com.airbnb.lottie.model.content.ShapeGroup
import com.airbnb.lottie.model.layer.Layer
import com.airbnb.lottie.value.Keyframe
import kotlin.properties.Delegates
-fun Modifier.withTransform(transform: LayerTransform): Modifier {
+fun Modifier.withTransform(transform: ComposeLottieTransform): Modifier {
if (transform.isIdentity) return this
// TODO: maybe use composed {}
@@ -22,7 +22,26 @@
)
}
-class LayerTransform(private val transform: AnimatableTransform?) {
+@Composable
+fun rememberTransform(layer: Layer): ComposeLottieTransform {
+ val animatableTransform = remember(layer) { layer.transform }
+ return rememberTransform(animatableTransform)
+}
+
+@Composable
+fun rememberTransform(shapeGroup: ShapeGroup): ComposeLottieTransform {
+ val animatableTransform = remember(shapeGroup) { shapeGroup.items.lastOrNull() as? AnimatableTransform }
+ return rememberTransform(animatableTransform)
+}
+
+@Composable
+fun rememberTransform(transform: AnimatableTransform?): ComposeLottieTransform {
+ val composeTransform = remember(transform) { ComposeLottieTransform(transform) }
+ composeTransform.progress = LocalLottieProgress.current
+ return composeTransform
+}
+
+class ComposeLottieTransform(private val transform: AnimatableTransform?) {
var progress by Delegates.observable(0f) { _, oldValue, newValue ->
if (oldValue == newValue) return@observable
updatePosition(newValue)
@@ -30,7 +49,7 @@
private var positionProgress = 0f
private var positionKeyframeIndex = 0
- private var _position = mutableStateOf(POINT_F)
+ private var _position = mutableStateOf(PointF())
val position: PointF by _position
val isIdentity get() = transform == null || position.equals(0f, 0f)
@@ -50,10 +69,6 @@
lerp(keyframe.startValue?.y ?: 0f, keyframe.endValue?.y ?: 0f, interpolatedProgress),
)
}
-
- companion object {
- private val POINT_F = PointF()
- }
}
private fun <T> List<Keyframe<T>>.findKeyframeIndex(progress: Float, currentKeyframeIndex: Int): Keyframe<T> {
diff --git a/lottie-compose/src/main/java/com/airbnb/lottie/compose/renderer/LottieComposeRenderer.kt b/lottie-compose/src/main/java/com/airbnb/lottie/compose/renderer/LottieComposeRenderer.kt
index 20cb2c0..c50b0d8 100644
--- a/lottie-compose/src/main/java/com/airbnb/lottie/compose/renderer/LottieComposeRenderer.kt
+++ b/lottie-compose/src/main/java/com/airbnb/lottie/compose/renderer/LottieComposeRenderer.kt
@@ -3,7 +3,6 @@
import android.graphics.PointF
import android.util.Log
import androidx.compose.foundation.Image
-import androidx.compose.foundation.layout.size
import androidx.compose.runtime.*
import androidx.compose.ui.Modifier
import androidx.compose.ui.graphics.Color
@@ -13,7 +12,6 @@
import com.airbnb.lottie.LottieComposition
import com.airbnb.lottie.compose.LottieAnimationState
import com.airbnb.lottie.compose.LottieCompositionResult
-import com.airbnb.lottie.model.animatable.AnimatableTransform
import com.airbnb.lottie.model.content.RectangleShape
import com.airbnb.lottie.model.content.ShapeFill
import com.airbnb.lottie.model.content.ShapeGroup
@@ -24,7 +22,11 @@
val LocalLottieProgress = compositionLocalOf { 0f }
@Composable
-fun ComposeLottieAnimation(compositionResult: LottieCompositionResult, state: LottieAnimationState) {
+fun ComposeLottieAnimation(
+ compositionResult: LottieCompositionResult,
+ state: LottieAnimationState,
+ modifier: Modifier = Modifier,
+) {
if (compositionResult !is LottieCompositionResult.Success) return
val composition = compositionResult.composition
@@ -62,8 +64,7 @@
Layer.LayerType.SHAPE -> Image(
shapeLayerPainter(composition, layer),
contentDescription = null,
- // TODO: use a different size
- modifier = Modifier.size(256.dp)
+ modifier = modifier,
)
else -> Unit
}
@@ -73,23 +74,20 @@
@Composable
fun shapeLayerPainter(composition: LottieComposition, layer: Layer): VectorPainter {
- val progress = LocalLottieProgress.current
- val animatableTransform = remember(layer) { layer.shapes.firstOrNull { it is AnimatableTransform } as? AnimatableTransform }
- val transform = LayerTransform(animatableTransform)
- LaunchedEffect(progress) {
- transform.progress = progress
- }
-
+ val transform = rememberTransform(layer)
return rememberVectorPainter(
- defaultWidth = 256.dp,
- defaultHeight = 256.dp,
+ defaultWidth = composition.bounds.width().dp,
+ defaultHeight = composition.bounds.height().dp,
+ viewportWidth = composition.bounds.width().toFloat(),
+ viewportHeight = composition.bounds.height().toFloat(),
) { viewportWidth, viewportHeight ->
+ Log.d("Gabe", "shapeLayerPainter ${transform.position}")
Group(
name = layer.layerName,
translationX = transform.position.x,
translationY = transform.position.y,
- scaleX = viewportWidth / composition.bounds.width(),
- scaleY = viewportHeight / composition.bounds.height(),
+ scaleX = composition.bounds.width() / viewportWidth,
+ scaleY = composition.bounds.height() / viewportHeight,
) {
PathData {
layer.shapes.forEach { shapeModel ->
@@ -106,18 +104,25 @@
@Composable
fun ComposeShapeGroup(shapeGroup: ShapeGroup) {
if (shapeGroup.isHidden || shapeGroup.items.isEmpty()) return
- val transform = remember(shapeGroup) { LayerTransform(shapeGroup.items.lastOrNull() as? AnimatableTransform) }
+ val transform = rememberTransform(shapeGroup)
val pathData = remember { mutableListOf<PathNode>() }
- val progress = LocalLottieProgress.current
- LaunchedEffect(progress) {
- transform.progress = progress
- }
- pathData.clear()
+
+ Log.d("Gabe", "ComposeShapeGroup ${transform.position}")
Group(
name = shapeGroup.name,
translationX = transform.position.x,
translationY = transform.position.y,
) {
+ // Reuse the list and clear it so that the backing array doesn't need to be recreated.
+ pathData.clear()
+ pathData += PathData {
+ moveTo(380f, 0f)
+ lineTo(400f, 0f)
+ lineTo(400f, 20f)
+ lineTo(380f, 20f)
+ lineTo(380f, 0f)
+ close()
+ }
for (model in shapeGroup.items) {
when (model) {
is RectangleShape -> {
@@ -127,7 +132,6 @@
ComposeShapeFill(model, pathData)
}
}
- Log.d("Gabe", "Drawing ${model::class.simpleName} $model")
}
}
}
@@ -148,19 +152,23 @@
lerp(sizeKeyframe.startValue?.y ?: 0f, sizeKeyframe.endValue?.y ?: 0f, interpolatedProgress),
)
+ val halfWidth = size.x / 2f
+ val halfHeight = size.y / 2f
+
return PathData {
- moveTo(0f, 0f)
- lineTo(size.x, 0f)
- lineTo(size.x, size.y)
- lineTo(0f, size.y)
- lineTo(0f, 0f)
+ moveTo(-halfWidth, -halfHeight)
+ lineTo(halfWidth, -halfHeight)
+ lineTo(halfWidth, halfHeight)
+ lineTo(-halfWidth, halfHeight)
+ lineTo(-halfWidth, -halfHeight)
close()
}
}
@Composable
fun ComposeShapeFill(fill: ShapeFill, pathData: List<PathNode>) {
- Log.d("Gabe", "Drawing fill with ${pathData.size} nodes")
+ val colorAnimation = fill.color?.keyframes ?: return
+
Path(
pathData,
// TODO: use the real color
diff --git a/lottie/src/main/java/com/airbnb/lottie/model/layer/Layer.java b/lottie/src/main/java/com/airbnb/lottie/model/layer/Layer.java
index 7bab3ab..fd4aa09 100644
--- a/lottie/src/main/java/com/airbnb/lottie/model/layer/Layer.java
+++ b/lottie/src/main/java/com/airbnb/lottie/model/layer/Layer.java
@@ -149,7 +149,7 @@
return shapes;
}
- AnimatableTransform getTransform() {
+ public AnimatableTransform getTransform() {
return transform;
}
diff --git a/lottie/src/main/java/com/airbnb/lottie/utils/Utils.java b/lottie/src/main/java/com/airbnb/lottie/utils/Utils.java
index baac4e5..136c1ee 100644
--- a/lottie/src/main/java/com/airbnb/lottie/utils/Utils.java
+++ b/lottie/src/main/java/com/airbnb/lottie/utils/Utils.java
@@ -252,6 +252,7 @@
}
public static float dpScale() {
+ if (true) return 1;
if (dpScale == -1) {
dpScale = Resources.getSystem().getDisplayMetrics().density;
}