Center the album art zoom instead of favoring the top-left

animation.Scale's pivot is fixed at the top-left corner with no way to
change it (an explicit origin param has no effect at all). Left alone,
that meant zooming in cropped progressively more off the bottom and right
than the top and left -- and since most of the animation's duration is
spent between keyframes rather than sitting at the symmetric rest state,
this read as a persistent "bottom and right are cropped" on the real
device.

Fix: pair Scale with a Translate that's a deterministic function of the
same scale value, re-centering the zoom so it crops evenly from all
sides. Verified against a synthetic calibration grid image (concentric
color rings + corner markers) rendered through the actual _art_widget
code -- confirms even margins at rest and symmetric cropping as it zooms
in, with no black border anywhere in the cycle. Safe because scale never
drops below 1.0 here; the earlier black-border bug came from combining
Translate with scale below 1.0, not from Translate itself.
This commit is contained in:
poprhythm committed 2026-10-01 21:57:18 -04:00
1 parent 60ba5f8a3b
commit 975ff6da28
1 file changed
+17 -3
+17 -3
View File
@@ -223,23 +223,37 @@ def _art_widget(art_bytes, zoom_scale):
# don't actually agree on framing despite both nominally being "scale # don't actually agree on framing despite both nominally being "scale
# 1.0". Using the same wrapper unconditionally guarantees the "off" # 1.0". Using the same wrapper unconditionally guarantees the "off"
# state and the rest state of every zoom level line up exactly. # state and the rest state of every zoom level line up exactly.
#
# animation.Scale's pivot is fixed at the top-left corner (confirmed
# empirically; an explicit `origin` param has no effect at all). Left
# alone, that means zooming in crops progressively *more* off the
# bottom and right than the top and left -- most of the animation's
# duration is spent between keyframes, not sitting at the symmetric
# rest state, so this read as a persistent bottom/right crop. Pairing
# Scale with a Translate that's a deterministic function of the same
# scale value (`shift`) re-centers the zoom so it crops evenly from
# all sides instead -- verified against a calibration grid image.
# This is safe because scale never drops below 1.0 here: the
# black-border bug from an earlier version came from combining
# Translate with scale *below* 1.0, not from Translate itself.
top_scale = max(zoom_scale, 1.0) top_scale = max(zoom_scale, 1.0)
shift = (1.0 - top_scale) * (ART_SIZE / 2.0)
zoom = animation.Transformation( zoom = animation.Transformation(
child = art, child = art,
duration = ART_ZOOM_DURATION, duration = ART_ZOOM_DURATION,
keyframes = [ keyframes = [
animation.Keyframe( animation.Keyframe(
percentage = 0.0, percentage = 0.0,
transforms = [animation.Scale(1.0, 1.0)], transforms = [animation.Scale(1.0, 1.0), animation.Translate(0, 0)],
), ),
animation.Keyframe( animation.Keyframe(
percentage = 0.5, percentage = 0.5,
transforms = [animation.Scale(top_scale, top_scale)], transforms = [animation.Scale(top_scale, top_scale), animation.Translate(shift, shift)],
curve = "ease_in_out", curve = "ease_in_out",
), ),
animation.Keyframe( animation.Keyframe(
percentage = 1.0, percentage = 1.0,
transforms = [animation.Scale(1.0, 1.0)], transforms = [animation.Scale(1.0, 1.0), animation.Translate(0, 0)],
curve = "ease_in_out", curve = "ease_in_out",
), ),
], ],
Reference in new issue
Block a user
Blocking a user prevents them from interacting with repositories, such as opening or commenting on pull requests or issues. Learn more about blocking a user.