web: draw each landing photo cut to its own box, not to its own shape

This commit is contained in:
2026-09-18 21:24:02 +07:00
parent 2da045f232
commit e57444e88b
+18 -5
View File
@@ -25,6 +25,10 @@ const SAMPLE = (n: number) => `/assets/samples/s${n}.jpg`;
// that smaller bitmap is what the tag carries, so a saved copy is a screen-sized
// one and not the uploader's 4000px original. The save gestures are turned off
// on top of it. `data-full` keeps the original address for tests and debugging.
//
// The redraw covers what `object-fit: cover` will show, not the whole frame: a
// 4:3 upload in a 4:5 card would otherwise be handed a stubby bitmap that the
// card had to blow up again — the soft thumbnails the strip used to show.
function Shot({ src, alt, filter }: { src: string; alt: string; filter?: string }) {
const tag = useRef<HTMLImageElement>(null);
const [shown, setShown] = useState(src);
@@ -33,16 +37,25 @@ function Shot({ src, alt, filter }: { src: string; alt: string; filter?: string
let alive = true;
const source = new Image();
source.onload = () => {
const box = tag.current?.clientWidth ?? 0;
const boxW = tag.current?.clientWidth ?? 0;
const boxH = tag.current?.clientHeight ?? 0;
const scale = Math.min(2, Math.max(1, window.devicePixelRatio || 1));
const width = Math.min(source.naturalWidth, Math.round(box * scale));
if (!alive || box === 0 || width >= source.naturalWidth) return;
const width = Math.round(boxW * scale);
const height = Math.round(boxH * scale);
if (!alive || width === 0 || height === 0) return;
if (width >= source.naturalWidth && height >= source.naturalHeight) return;
// The centred slice `cover` would show, cut to exactly the box: scale up
// to fill both sides, keep the middle of the overflow.
const grow = Math.max(width / source.naturalWidth, height / source.naturalHeight);
const sw = width / grow;
const sh = height / grow;
const canvas = document.createElement('canvas');
canvas.width = width;
canvas.height = Math.max(1, Math.round((source.naturalHeight * width) / source.naturalWidth));
canvas.height = height;
const ctx = canvas.getContext('2d');
if (!ctx) return;
ctx.drawImage(source, 0, 0, canvas.width, canvas.height);
ctx.imageSmoothingQuality = 'high';
ctx.drawImage(source, (source.naturalWidth - sw) / 2, (source.naturalHeight - sh) / 2, sw, sh, 0, 0, width, height);
setShown(canvas.toDataURL('image/jpeg', 0.85));
};
source.src = src;