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
		Add a link
		
	
		Reference in a new issue