diff --git a/web/MediaField.tsx b/web/MediaField.tsx index 901fa37..5739ee3 100644 --- a/web/MediaField.tsx +++ b/web/MediaField.tsx @@ -187,12 +187,22 @@ export const MediaField = ({ to. That is also the behaviour somebody expects from a `+`: the middle of what I am looking at stays where it is. */ - const zoomBy = (factor: number) => { + const zoomBy = (step: number) => { if (!current || !natural || !onCropChange) return; const base = centredCrop(natural.w / natural.h, slotAspect); - // Fit is the floor — there is no more picture — and 8x is where a rendition runs out of pixels. - const zoom = Math.min(8, Math.max(1, (base.w / current.w) * factor)); + /* + A tenth per press, ADDED rather than multiplied. + + A factor compounds: the same button is a 0.25x step at fit and a 1.75x step at 7x, so the first + press does almost nothing and the last one throws the picture across the frame. A fixed step is + the same amount of change every time, which is what somebody nudging a crop into place is asking + for — and it makes the number beside the buttons predictable, which a factor never is. + + Fit is the floor — there is no more picture — and 8x is where a rendition runs out of pixels. + Rounded, because floating point turns 1 + 0.1 + 0.1 into 1.2000000000000002 and the label shows it. + */ + const zoom = Math.round(Math.min(8, Math.max(1, (base.w / current.w) + step)) * 10) / 10; const w = base.w / zoom; const h = base.h / zoom; @@ -348,7 +358,7 @@ export const MediaField = ({ size="sm" variant="ghost" className="size-6 p-0" - onClick={() => zoomBy(1 / 1.25)} + onClick={() => zoomBy(-0.1)} disabled={zoomLevel <= 1.001} title="Zoom out" > @@ -363,7 +373,7 @@ export const MediaField = ({ size="sm" variant="ghost" className="size-6 p-0" - onClick={() => zoomBy(1.25)} + onClick={() => zoomBy(0.1)} disabled={zoomLevel >= 7.99} title="Zoom in" >