fix(zoom): keep aspect ratio during the avatar zoom-in animation

The grow animation used independent scaleX/scaleY derived from the source
rect (square avatar) and the image rect (often landscape), so at the start
of the transition the full image was squashed into the avatar's square
footprint before lerping back to its real proportions.

Switches to a uniform scale based on max(src.width/img.width,
src.height/img.height) and centers the image on the tapped rect. The image
now keeps its aspect ratio throughout, covering the source rect in at
least one dimension at progress=0 and growing uniformly to fullscreen.

Also extends the entry guard to require non-zero source rect; without it
a not-yet-measured thumbnail would yield startScale=0 and collapse the
first frame to an invisible point before the animation expanded it.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
davotoula
2026-04-25 17:42:19 +02:00
parent 1876941d8f
commit 5861e3a7e5
@@ -274,15 +274,22 @@ private fun DialogContent(
.graphicsLayer {
val src = sourceBounds
val img = imageBounds
if (src != null && img != null && img.width > 0f && img.height > 0f) {
if (src != null && img != null &&
src.width > 0f && src.height > 0f &&
img.width > 0f && img.height > 0f
) {
transformOrigin = TransformOrigin(0f, 0f)
val startScaleX = src.width / img.width
val startScaleY = src.height / img.height
// Uniform scale so non-square images keep their aspect ratio during
// the grow animation. max() so the image covers the source rect in
// at least one dimension; the other overflows centered on the tap.
val startScale = maxOf(src.width / img.width, src.height / img.height)
val srcCenter = src.center
val imgCenter = img.center
val p = progress()
scaleX = lerp(startScaleX, 1f, p)
scaleY = lerp(startScaleY, 1f, p)
translationX = lerp(src.left - img.left * startScaleX, 0f, p)
translationY = lerp(src.top - img.top * startScaleY, 0f, p)
scaleX = lerp(startScale, 1f, p)
scaleY = lerp(startScale, 1f, p)
translationX = lerp(srcCenter.x - startScale * imgCenter.x, 0f, p)
translationY = lerp(srcCenter.y - startScale * imgCenter.y, 0f, p)
} else {
// No source bounds: fall back to a plain fade.
alpha = progress()