cf70a197cd
Befund aus dem Browser-Rundgang: example.com setzt `margin: 15vh` — mit 3000 px Vorschauhoehe lag die Ueberschrift bei y 450, in der Kachel mit 720 px Rahmenhoehe bei y 108. Der in der Vorschau gewaehlte Ausschnitt zeigte in der Kachel also etwas anderes. Die Kachel nutzt jetzt dieselbe Layouthoehe wie die Vorschau (3000), damit vh-relative Seiten identisch umbrechen; der Rest wird ohnehin weggeschnitten. Vorschau: `overflow-x-hidden`, die Eckgriffe am rechten Rand erzeugten einen 4-px-Querbalken. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
89 lines
3.5 KiB
TypeScript
89 lines
3.5 KiB
TypeScript
/**
|
|
* xframe-crop — reine Geometrie des XFrame-Ausschnitts (quick-260922-ge2),
|
|
* ohne React, fuer Kachel UND Vorschau.
|
|
*
|
|
* Einpassen (contain) und Zentrieren: der Ausschnitt muss in der Kachel
|
|
* immer GANZ sichtbar sein, und das Seitenverhaeltnis der Kachel stimmt
|
|
* praktisch nie mit dem des Ausschnitts ueberein — deshalb der kleinere
|
|
* der beiden Massstaebe (darf > 1 sein: ein kleiner Ausschnitt in einer
|
|
* grossen Kachel wird vergroessert) und die Zentrierung im Rest.
|
|
*
|
|
* `frameHeight` ist IMMER die Vorschauhoehe (3000 Seitenpixel), nicht
|
|
* "Unterkante des Ausschnitts": viele Seiten setzen Masse relativ zur
|
|
* Fensterhoehe (`vh`, `100vh`-Zentrierung, `margin: 15vh` bei example.com).
|
|
* Haetten Vorschau und Kachel verschiedene Rahmenhoehen, laege derselbe
|
|
* Inhalt an verschiedenen Stellen — der in der Vorschau gewaehlte Ausschnitt
|
|
* zeigte in der Kachel etwas anderes (Browser-Rundgang 22.09.2026: Ueberschrift
|
|
* in der Vorschau bei y 450, in der Kachel mit 720 px Rahmen bei y 108).
|
|
* Gleiche Hoehe = gleiches Layout; der Rest wird ohnehin weggeschnitten.
|
|
*
|
|
* Ziehmathematik der Vorschau: `move` verschiebt bei fester Groesse und
|
|
* klemmt an den Seitenraendern; die Eckmodi bewegen nur die zwei Kanten
|
|
* ihrer Ecke — die gegenueberliegende Ecke bleibt stehen, damit sich der
|
|
* Rahmen so verhaelt, wie man es von jedem Bildeditor kennt; die
|
|
* Mindestgroesse wird an der BEWEGTEN Kante gehalten. Das Ergebnis geht
|
|
* durch `clampXframeCrop` (eine Klemmregel fuer alles, T-GE2-05).
|
|
*/
|
|
|
|
import {
|
|
XFRAME_CROP_MIN_H,
|
|
XFRAME_CROP_MIN_W,
|
|
XFRAME_PAGE_WIDTH,
|
|
type XframeCrop,
|
|
clampXframeCrop,
|
|
} from './xframe-config';
|
|
|
|
/**
|
|
* Hoehe der Seite in Seitenpixeln — in der Vorschau (Stage, Ziehen) UND als
|
|
* Layouthoehe des Rahmens in der Kachel (siehe Kopfkommentar); 4000 bleibt
|
|
* die Speichergrenze fuer `crop.y + crop.h`.
|
|
*/
|
|
export const XFRAME_PREVIEW_PAGE_HEIGHT = 3000;
|
|
|
|
export type XframeDragMode = 'move' | 'nw' | 'ne' | 'sw' | 'se';
|
|
|
|
export interface XframeCropLayout {
|
|
/** 0 = Kachel noch nicht gemessen -> nichts rendern. */
|
|
scale: number;
|
|
left: number;
|
|
top: number;
|
|
frameHeight: number;
|
|
}
|
|
|
|
export function computeCropLayout(crop: XframeCrop, tileW: number, tileH: number): XframeCropLayout {
|
|
const frameHeight = XFRAME_PREVIEW_PAGE_HEIGHT;
|
|
if (tileW <= 0 || tileH <= 0) return { scale: 0, left: 0, top: 0, frameHeight };
|
|
const scale = Math.min(tileW / crop.w, tileH / crop.h);
|
|
return {
|
|
scale,
|
|
left: (tileW - crop.w * scale) / 2,
|
|
top: (tileH - crop.h * scale) / 2,
|
|
frameHeight,
|
|
};
|
|
}
|
|
|
|
export function applyCropDrag(mode: XframeDragMode, start: XframeCrop, dxPage: number, dyPage: number): XframeCrop {
|
|
let left = start.x;
|
|
let top = start.y;
|
|
let right = start.x + start.w;
|
|
let bottom = start.y + start.h;
|
|
|
|
if (mode === 'move') {
|
|
left = Math.min(Math.max(0, left + dxPage), XFRAME_PAGE_WIDTH - start.w);
|
|
top = Math.min(Math.max(0, top + dyPage), Math.max(0, XFRAME_PREVIEW_PAGE_HEIGHT - start.h));
|
|
return clampXframeCrop({ x: left, y: top, w: start.w, h: start.h });
|
|
}
|
|
|
|
if (mode === 'nw' || mode === 'sw') {
|
|
left = Math.min(Math.max(0, left + dxPage), right - XFRAME_CROP_MIN_W);
|
|
} else {
|
|
right = Math.max(Math.min(XFRAME_PAGE_WIDTH, right + dxPage), left + XFRAME_CROP_MIN_W);
|
|
}
|
|
if (mode === 'nw' || mode === 'ne') {
|
|
top = Math.min(Math.max(0, top + dyPage), bottom - XFRAME_CROP_MIN_H);
|
|
} else {
|
|
bottom = Math.max(Math.min(XFRAME_PREVIEW_PAGE_HEIGHT, bottom + dyPage), top + XFRAME_CROP_MIN_H);
|
|
}
|
|
return clampXframeCrop({ x: left, y: top, w: right - left, h: bottom - top });
|
|
}
|