Revert jerky zoom fix; cap strong zoom level instead

Dividing the centering shift by top_scale (previous commit) avoided the
strong-zoom black border, but made the shift a nonlinear function of the
scale value being interpolated -- mid-animation, the pan no longer
tracked the zoom at a constant rate, which read as jerky/skippy motion.

Reverted shift to the original linear formula, (1-s)*(ART_SIZE/2), which
moves smoothly in lockstep with Scale's own eased interpolation. Instead,
capped ZOOM_LEVELS["strong"] at 1.45 (down from 1.7) -- the highest value
where this plain linear formula stays safely inside the art's bounds,
verified with the calibration tool (no black border across the full
animation cycle, at both 1.45x and against a live station).
This commit is contained in:
poprhythm committed 2026-10-01 22:34:45 -04:00
1 parent 9f2216c692
commit 5f42319e26
2 files changed
+19 -17

No files matched your search

+18 -16
View File
@@ -91,12 +91,16 @@ COLOR_SCHEMES = {
DEFAULT_COLOR_SCHEME = "cyberpunk" # matches the station's own site theme
# How far in the album art zoom goes, relative to its fully-zoomed-out rest
# state (see _art_widget below for why scale never goes below 1.0).
# state (see _art_widget below for why scale never goes below 1.0). "strong"
# is capped at 1.45, not pushed further: with the centering Translate in
# _art_widget, empirically anything above ~1.46 starts revealing a real
# black border in the bottom-right corner (verified with the calibration
# tool) -- this is the safe ceiling, not an arbitrary choice.
ZOOM_LEVELS = {
"off": 1.0,
"subtle": 1.15,
"moderate": 1.4,
"strong": 1.7,
"strong": 1.45,
}
DEFAULT_ZOOM_LEVEL = "moderate"
@@ -233,21 +237,19 @@ def _art_widget(art_bytes, zoom_scale):
# Scale with a Translate re-centers the zoom so it crops evenly from
# all sides instead -- verified against a calibration grid image.
#
# The naive "final pixel shift" formula, (1-s)*(ART_SIZE/2), looked
# right at a moderate zoom (1.4x) but pushed past the image's actual
# edge at a strong one (1.7x) -- a real, on-device black border in the
# bottom-right corner. That means Translate isn't applied in final
# output pixels here; empirically, dividing by `top_scale` keeps the
# crop inside the image's bounds at every zoom level this app offers
# (verified via the calibration tool at both 1.4x and 1.7x), at the
# cost of the *most* extreme (1.7x) zoom being very slightly
# off-center rather than perfectly centered -- a trade worth making
# since going outside the art's bounds is worse than a slight
# asymmetry. 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.
# shift = (1-s)*(ART_SIZE/2) is linear in the scale value, which keeps
# it moving smoothly in lockstep with Scale's own eased interpolation
# throughout the animation. A tempting "fix" for a black-border bug at
# strong zoom was dividing this by `top_scale` to pull the shift in at
# high scale values -- that avoided the border, but made the shift a
# *nonlinear* function of the scale being interpolated, so partway
# through the animation the pan no longer tracked the zoom at a
# constant rate -- visibly jerky. Reverted: instead, ZOOM_LEVELS caps
# "strong" at a value where this plain linear formula is already safe
# (verified via the calibration tool), rather than changing the
# formula's shape.
top_scale = max(zoom_scale, 1.0)
shift = (1.0 - top_scale) * (ART_SIZE / 2.0) / top_scale
shift = (1.0 - top_scale) * (ART_SIZE / 2.0)
zoom = animation.Transformation(
child = art,
duration = ART_ZOOM_DURATION,
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.