web: draw each landing photo cut to its own box, not to its own shape
This commit is contained in:
@@ -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
|
// 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
|
// 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.
|
// 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 }) {
|
function Shot({ src, alt, filter }: { src: string; alt: string; filter?: string }) {
|
||||||
const tag = useRef<HTMLImageElement>(null);
|
const tag = useRef<HTMLImageElement>(null);
|
||||||
const [shown, setShown] = useState(src);
|
const [shown, setShown] = useState(src);
|
||||||
@@ -33,16 +37,25 @@ function Shot({ src, alt, filter }: { src: string; alt: string; filter?: string
|
|||||||
let alive = true;
|
let alive = true;
|
||||||
const source = new Image();
|
const source = new Image();
|
||||||
source.onload = () => {
|
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 scale = Math.min(2, Math.max(1, window.devicePixelRatio || 1));
|
||||||
const width = Math.min(source.naturalWidth, Math.round(box * scale));
|
const width = Math.round(boxW * scale);
|
||||||
if (!alive || box === 0 || width >= source.naturalWidth) return;
|
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');
|
const canvas = document.createElement('canvas');
|
||||||
canvas.width = width;
|
canvas.width = width;
|
||||||
canvas.height = Math.max(1, Math.round((source.naturalHeight * width) / source.naturalWidth));
|
canvas.height = height;
|
||||||
const ctx = canvas.getContext('2d');
|
const ctx = canvas.getContext('2d');
|
||||||
if (!ctx) return;
|
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));
|
setShown(canvas.toDataURL('image/jpeg', 0.85));
|
||||||
};
|
};
|
||||||
source.src = src;
|
source.src = src;
|
||||||
|
|||||||
Reference in New Issue
Block a user