WIP: MediaQuickview: rewritten full-screen media viewer #50
2 changed files with 61 additions and 7 deletions
|
@ -47,8 +47,6 @@
|
|||
max-width: 100vw;
|
||||
max-height: 100vh;
|
||||
contain: strict;
|
||||
display: grid;
|
||||
place-items: center;
|
||||
|
||||
cursor: grab;
|
||||
|
||||
|
|
|
@ -56,19 +56,39 @@ export function createMediaQuickview() {
|
|||
return createCallback(renderIsolateMediaQuickview);
|
||||
}
|
||||
|
||||
type VisualMediaMeta = {
|
||||
width: number;
|
||||
height: number;
|
||||
};
|
||||
|
||||
function ImagePage(props: {
|
||||
src: string;
|
||||
alt?: string;
|
||||
scale: number;
|
||||
offsetX: number;
|
||||
offsetY: number;
|
||||
|
||||
onMetadata?(metadata: VisualMediaMeta): void;
|
||||
}) {
|
||||
const [loaded, setLoaded] = createSignal(false);
|
||||
|
||||
return (
|
||||
<img
|
||||
onLoad={({ currentTarget }) => {
|
||||
const { naturalHeight, naturalWidth } = currentTarget;
|
||||
|
||||
props.onMetadata?.({
|
||||
width: naturalWidth,
|
||||
height: naturalHeight,
|
||||
});
|
||||
|
||||
setLoaded(true);
|
||||
}}
|
||||
src={props.src}
|
||||
alt={props.alt}
|
||||
style={{
|
||||
transform: `scale(${props.scale}) translateX(${-props.offsetX}px) translateY(${-props.offsetY}px)`,
|
||||
opacity: loaded() ? 1 : 0,
|
||||
}}
|
||||
></img>
|
||||
);
|
||||
|
@ -88,10 +108,6 @@ export type MediaQuickviewProps = {
|
|||
onClose?(): void;
|
||||
};
|
||||
|
||||
function clamp<T extends number>(value: T, min: T, max: T) {
|
||||
return Math.max(Math.min(value, max), min);
|
||||
}
|
||||
|
||||
const MediaQuickview: Component<MediaQuickviewProps> = (props) => {
|
||||
let root: HTMLDialogElement;
|
||||
|
||||
|
@ -126,6 +142,11 @@ const MediaQuickview: Component<MediaQuickviewProps> = (props) => {
|
|||
* positive = the top edge move towards top
|
||||
*/
|
||||
offsetY: number;
|
||||
|
||||
size?: {
|
||||
width: number;
|
||||
height: number;
|
||||
};
|
||||
}[],
|
||||
);
|
||||
|
||||
|
@ -185,7 +206,7 @@ const MediaQuickview: Component<MediaQuickviewProps> = (props) => {
|
|||
return;
|
||||
}
|
||||
const dx = movOriginX - event.clientX;
|
||||
const dy = movOriginY - event.clientY ;
|
||||
const dy = movOriginY - event.clientY;
|
||||
|
||||
setTransformations(index, ({ offsetX, offsetY }) => {
|
||||
return {
|
||||
|
@ -202,6 +223,40 @@ const MediaQuickview: Component<MediaQuickviewProps> = (props) => {
|
|||
setIsMoving(false);
|
||||
};
|
||||
|
||||
const recenter = (index: number) => {
|
||||
const sz = transformations[index].size;
|
||||
if (!sz) return;
|
||||
const { width, height } = sz;
|
||||
|
||||
const xscale = Math.min(window.innerWidth / width, 1);
|
||||
const yscale = Math.min(window.innerHeight / height, 1);
|
||||
|
||||
const finalScale = Math.min(xscale, yscale);
|
||||
|
||||
const nheight = height * finalScale;
|
||||
const top = (window.innerHeight - nheight) / 2;
|
||||
|
||||
const nwidth = width * finalScale;
|
||||
const left = (window.innerWidth - nwidth) / 2;
|
||||
|
||||
setTransformations(index, {
|
||||
scale: finalScale,
|
||||
offsetX: -left,
|
||||
offsetY: -top,
|
||||
});
|
||||
};
|
||||
|
||||
const onMetadata = (index: number, { width, height }: VisualMediaMeta) => {
|
||||
setTransformations(index, {
|
||||
size: {
|
||||
width,
|
||||
height,
|
||||
},
|
||||
});
|
||||
|
||||
recenter(index);
|
||||
};
|
||||
|
||||
return (
|
||||
<dialog
|
||||
ref={(e) => {
|
||||
|
@ -254,6 +309,7 @@ const MediaQuickview: Component<MediaQuickviewProps> = (props) => {
|
|||
<Switch>
|
||||
<Match when={item().cat === "image"}>
|
||||
<ImagePage
|
||||
onMetadata={(m) => onMetadata(index, m)}
|
||||
src={item().src}
|
||||
alt={item().alt}
|
||||
scale={transformationGetOrSetDefault(index).scale}
|
||||
|
|
Loading…
Add table
Reference in a new issue