f79d56a82d
Reported: fully zoomed in, couldn't pan left/right at all — stuck dead-center. My previous fix pinned the zoom's transform-origin at a fixed 50% 50% (the frame's own center) specifically to fix a *different* bug (the head of a portrait being unreachable). That traded one bug for another: with a fixed center, object-position's own pan formula only has room to move in whichever axis has overflow at zoom=1. For a portrait photo in this landscape frame, width is the *exact*-fit axis at zoom=1 — literally zero horizontal slack — so there was nothing for a fixed-center zoom to magnify away from, and horizontal dragging computed a division by (essentially) zero, guarded down to a hard no-op. The actual fix: transform-origin should match object-position (X% Y%), not stay fixed at center. Re-derived from there — with a matching origin, box position (X%, Y%) always shows exactly natural-image point (X/100*naturalW, Y/100*naturalH), at any zoom, in either axis, with no zero-slack case at all. Rewrote the drag-sensitivity and reference- rectangle math to match this (both simplify to plain linear formulas). Verified in the standalone test harness with the same portrait test image that reproduces the zero-slack case: dragging now works at any zoom level in both axes, and reaching an image edge (the original bug) still works too — tested both scenarios explicitly before deploying, since the first fix silently broke the second. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
34 lines
1.6 KiB
PHP
34 lines
1.6 KiB
PHP
<?php
|
|
/**
|
|
* includes/photo.php — shared helper for the photo reposition/zoom feature.
|
|
*/
|
|
|
|
/**
|
|
* Build the inline style="" value that applies a session/novena_group row's
|
|
* stored crop (photo_focal_x/photo_focal_y/photo_zoom) to an <img> using
|
|
* object-fit: cover. Falls back to 50/50/1 (dead center, no zoom — today's
|
|
* default behavior) when the row predates this feature or the values are
|
|
* otherwise missing, so old photos render exactly as they always have.
|
|
*/
|
|
function photo_crop_style(array $row): string {
|
|
$x = isset($row['photo_focal_x']) && $row['photo_focal_x'] !== null ? (float)$row['photo_focal_x'] : 50.0;
|
|
$y = isset($row['photo_focal_y']) && $row['photo_focal_y'] !== null ? (float)$row['photo_focal_y'] : 50.0;
|
|
$zoom = isset($row['photo_zoom']) && $row['photo_zoom'] !== null ? (float)$row['photo_zoom'] : 1.0;
|
|
|
|
$x = max(0, min(100, $x));
|
|
$y = max(0, min(100, $y));
|
|
$zoom = max(1, min(3, $zoom));
|
|
|
|
// transform-origin matches object-position — see photo-crop.js's file
|
|
// header for why this (rather than a fixed 50% 50%) is required: a
|
|
// fixed center has zero pan room on whichever axis has no overflow at
|
|
// zoom=1 (common for a portrait photo in a landscape box), so panning
|
|
// that axis goes dead the moment you zoom in. Matching the origin to
|
|
// (X%, Y%) pins that natural-image point to the same box position at
|
|
// any zoom, with no such zero-slack case in either axis.
|
|
return sprintf(
|
|
'object-position:%.2f%% %.2f%%;transform:scale(%.3f);transform-origin:%.2f%% %.2f%%;',
|
|
$x, $y, $zoom, $x, $y
|
|
);
|
|
}
|