Revert "frame a photograph on the page, at the size it is printed"
This reverts commit 812371b64a.
This commit is contained in:
+44
-8
@@ -3,7 +3,6 @@ import { ImageOff, Minus, Pencil, Plus, Trash2, Upload } from 'lucide-react';
|
||||
import { Button } from '@/components/ui/button';
|
||||
import { MediaPicker } from './MediaPicker';
|
||||
import { originalUrl, posterUrl } from './thumbnail';
|
||||
import { centredCrop, formatCrop, parseCrop, rectToBox, type Rect as Crop } from './crop';
|
||||
|
||||
/**
|
||||
* One picture or video on the page, shown at the shape the page will show it.
|
||||
@@ -37,11 +36,7 @@ import { centredCrop, formatCrop, parseCrop, rectToBox, type Rect as Crop } from
|
||||
*
|
||||
* A photograph is rarely the shape of the slot it goes in, so something has to decide which part
|
||||
* survives. The site's answer is a `crop`, stored as `x,y,w,h` in percentages of the ORIGINAL, which
|
||||
* the page draws as a rectangle and the build cuts into real pixels.
|
||||
*
|
||||
* The same picture can also be framed ON the page, at the size it is printed — see framing.ts. This
|
||||
* field stays because it is where the file is CHOSEN, because it carries the zoom, and because a
|
||||
* gallery slot has no page to drag on.
|
||||
* `render` turns into an `object-position`.
|
||||
*
|
||||
* Here that is a drag. The visible window is the largest rectangle of the slot's shape that fits inside
|
||||
* the picture — so one axis has slack and the other has none — and dragging slides the window along
|
||||
@@ -63,6 +58,39 @@ import { centredCrop, formatCrop, parseCrop, rectToBox, type Rect as Crop } from
|
||||
* There is no third option, so the wheel belongs to the page and the zoom is `+` and `−`.
|
||||
*/
|
||||
|
||||
/** `x,y,w,h` in percentages of the original. The site's format — see parseCrop in media/render.ts. */
|
||||
type Crop = { x: number; y: number; w: number; h: number };
|
||||
|
||||
const parseCrop = (value: string | null | undefined): Crop | null => {
|
||||
const parts = (value ?? '').split(',').map(Number);
|
||||
if (parts.length !== 4 || parts.some((n) => !Number.isFinite(n))) return null;
|
||||
const [x, y, w, h] = parts as [number, number, number, number];
|
||||
return w > 0 && h > 0 ? { x, y, w, h } : null;
|
||||
};
|
||||
|
||||
const formatCrop = (crop: Crop): string =>
|
||||
[crop.x, crop.y, crop.w, crop.h].map((n) => Math.round(n * 10) / 10).join(',');
|
||||
|
||||
/**
|
||||
* The window the slot shows of a picture, centred, when nothing has been chosen.
|
||||
*
|
||||
* The largest rectangle of the SLOT's ratio that fits inside the picture. One of the two dimensions is
|
||||
* always 100 — you cannot show more of a picture than there is.
|
||||
*/
|
||||
const centredCrop = (imageAspect: number, slotAspect: number): Crop =>
|
||||
imageAspect > slotAspect
|
||||
? { x: (100 - (slotAspect / imageAspect) * 100) / 2, y: 0, w: (slotAspect / imageAspect) * 100, h: 100 }
|
||||
: { x: 0, y: (100 - (imageAspect / slotAspect) * 100) / 2, w: 100, h: (imageAspect / slotAspect) * 100 };
|
||||
|
||||
/** Crop to CSS. The same arithmetic as cropToPosition in the site's media/render.ts, and it must stay so. */
|
||||
const toObjectPosition = (crop: Crop): string => {
|
||||
const along = (start: number, size: number) => {
|
||||
const remaining = 100 - size;
|
||||
return remaining <= 0 ? 50 : Math.min(100, Math.max(0, (start / remaining) * 100));
|
||||
};
|
||||
return `${along(crop.x, crop.w).toFixed(1)}% ${along(crop.y, crop.h).toFixed(1)}%`;
|
||||
};
|
||||
|
||||
const DRAG_TYPE = 'application/x-officer-files';
|
||||
|
||||
export const MediaField = ({
|
||||
@@ -275,8 +303,16 @@ export const MediaField = ({
|
||||
drawn.
|
||||
*/
|
||||
className={`absolute max-w-none touch-none ${framable ? 'cursor-grab active:cursor-grabbing' : ''}`}
|
||||
// `rectToBox` — the same four sums the page uses. See crop.ts for why they are shared.
|
||||
style={current ? rectToBox(current) : { width: '100%', height: '100%', objectFit: 'cover' }}
|
||||
style={
|
||||
current
|
||||
? {
|
||||
width: `${(100 / current.w) * 100}%`,
|
||||
height: `${(100 / current.h) * 100}%`,
|
||||
left: `${-(current.x / current.w) * 100}%`,
|
||||
top: `${-(current.y / current.h) * 100}%`,
|
||||
}
|
||||
: { width: '100%', height: '100%', objectFit: 'cover' }
|
||||
}
|
||||
draggable={false}
|
||||
onPointerDown={onPointerDown}
|
||||
onPointerMove={onPointerMove}
|
||||
|
||||
Reference in New Issue
Block a user