Show library photos at their own aspect instead of cropping to the ratio band

This commit is contained in:
2026-09-10 11:28:54 +07:00
parent 21e4932249
commit 4ad26b514e
2 changed files with 40 additions and 28 deletions
+28 -24
View File
@@ -216,6 +216,7 @@ const Viewfinder = forwardRef<ViewfinderHandle, ViewfinderProps>(function Viewfi
// every consumer below degrades to the old fullscreen behavior untouched.
const ratioWH = aspectRatio === 'full' ? null : ASPECT_RATIO_W_H[aspectRatio];
const regionBand =
mode === 'camera' &&
ratioWH !== null &&
(selectedFrame === 'none' || selectedFrame === 'classic-white' || selectedFrame === 'cinematic');
const region = useMemo(() => {
@@ -936,6 +937,17 @@ const Viewfinder = forwardRef<ViewfinderHandle, ViewfinderProps>(function Viewfi
const dh = h * s;
return { dx: (vw - dw) / 2, dy: (vh - dh) / 2, dw, dh };
}, [skiaImage, vw, vh]);
// Photo display rect + draw fit in the plain library viewer: the full photo
// is contain-fitted to the screen (fill into its own aspect rect — no
// distortion, identical to the old fullscreen contain). The ratio band only
// shapes the CAMERA composition; a library export is the photo as framed.
const dispRect = regionBand
? { dx: region.x, dy: region.y, dw: region.w, dh: region.h }
: imageFitRect;
const dispFit: 'cover' | 'fill' = regionBand ? 'cover' : 'fill';
// Rect the frame border / cinematic bars / grain hug: the ratio band on the
// camera, the contain rect of the loaded library photo, fullscreen otherwise.
const frameRect = mode === 'library' && dispRect ? dispRect : region;
// Polaroid print geometry (JS/overlay space) for the CURRENT mode. The card
// always fills/holds to the screen container; the photo WINDOW keeps the
// *composition* aspect: with a ratio active that ratio (window content is
@@ -1323,10 +1335,10 @@ const Viewfinder = forwardRef<ViewfinderHandle, ViewfinderProps>(function Viewfi
};
})();
// Border/bars scale with the composition region so framed exports stay
// proportional to the cropped photo (region == fullscreen when no ratio).
const borderWidth = Math.min(region.w, region.h) * 0.05;
const barHeight = region.h * 0.12;
// Border/bars scale with the visible photo rect so a framed export stays
// proportional to the picture (band on the camera, contain rect in library).
const borderWidth = Math.min(frameRect.w, frameRect.h) * 0.05;
const barHeight = frameRect.h * 0.12;
const renderFrameOverlay = () => {
if (selectedFrame === 'none') return null;
@@ -1395,24 +1407,24 @@ const Viewfinder = forwardRef<ViewfinderHandle, ViewfinderProps>(function Viewfi
}
if (selectedFrame === 'classic-white') {
// Border drawn on the composition region (region == fullscreen when no
// ratio band), so it always sits at the photo's edge.
// Border drawn on the visible photo rect (frameRect), so it always sits
// at the picture's edge — including the letterboxed library viewer.
return (
<Group color="white">
<Rect x={region.x} y={region.y} width={region.w} height={borderWidth} />
<Rect x={region.x} y={region.y + region.h - borderWidth} width={region.w} height={borderWidth} />
<Rect x={region.x} y={region.y} width={borderWidth} height={region.h} />
<Rect x={region.x + region.w - borderWidth} y={region.y} width={borderWidth} height={region.h} />
<Rect x={frameRect.x} y={frameRect.y} width={frameRect.w} height={borderWidth} />
<Rect x={frameRect.x} y={frameRect.y + frameRect.h - borderWidth} width={frameRect.w} height={borderWidth} />
<Rect x={frameRect.x} y={frameRect.y} width={borderWidth} height={frameRect.h} />
<Rect x={frameRect.x + frameRect.w - borderWidth} y={frameRect.y} width={borderWidth} height={frameRect.h} />
</Group>
);
}
if (selectedFrame === 'cinematic') {
// Letterbox bars hug the composition region's top/bottom edges.
// Letterbox bars hug the visible photo rect's top/bottom edges.
return (
<Group color="black">
<Rect x={region.x} y={region.y} width={region.w} height={barHeight} />
<Rect x={region.x} y={region.y + region.h - barHeight} width={region.w} height={barHeight} />
<Rect x={frameRect.x} y={frameRect.y} width={frameRect.w} height={barHeight} />
<Rect x={frameRect.x} y={frameRect.y + frameRect.h - barHeight} width={frameRect.w} height={barHeight} />
</Group>
);
}
@@ -1706,14 +1718,6 @@ const Viewfinder = forwardRef<ViewfinderHandle, ViewfinderProps>(function Viewfi
toneParams[0] !== 0 || toneParams[1] !== 0 || toneParams[2] !== 0 || toneParams[3] !== 0;
const cinemaOn = cinemaEffect != null && !!cinemaParams;
const useShaderPass = !!imageFitRect && ((toneEffect != null && toneOn) || cinemaOn);
// Photo display rect + draw fit in the plain library viewer: with a ratio
// band the photo is center-cropped (cover) into the composition region;
// otherwise the full photo is contain-fitted to the screen (fill into its
// own aspect rect — no distortion, identical to the old fullscreen contain).
const dispRect = regionBand
? { dx: region.x, dy: region.y, dw: region.w, dh: region.h }
: imageFitRect;
const dispFit: 'cover' | 'fill' = regionBand ? 'cover' : 'fill';
// RETRO POLAROID (library): the whole picture is contain-fitted into the
// card's photo window — nothing is masked or cropped, and the card is
@@ -1943,9 +1947,9 @@ const Viewfinder = forwardRef<ViewfinderHandle, ViewfinderProps>(function Viewfi
</Group>
{grainOpacity > 0 && noiseEffect && (
<Group blendMode="overlay">
{/* Grain covers the whole photo area — the composition region
under a ratio band, the full screen otherwise. */}
<Rect x={region.x} y={region.y} width={region.w} height={region.h} opacity={grainOpacity}>
{/* Grain covers the whole photo area — the contain rect of the
library photo, the ratio band on the camera. */}
<Rect x={frameRect.x} y={frameRect.y} width={frameRect.w} height={frameRect.h} opacity={grainOpacity}>
<Shader source={noiseEffect} />
</Rect>
</Group>