From 2cfb298f4a3b4d83e28020d3ee46878ed6969272 Mon Sep 17 00:00:00 2001 From: Supertiger Date: Fri, 24 Jan 2025 12:52:38 +0000 Subject: [PATCH] Add view transition to image embed --- src/common/transitionViewIfSupported.ts | 7 ++++ src/components/ui/ImageEmbed.tsx | 50 +++++++++++++++++-------- src/components/ui/ImagePreviewModal.tsx | 5 ++- 3 files changed, 45 insertions(+), 17 deletions(-) create mode 100644 src/common/transitionViewIfSupported.ts diff --git a/src/common/transitionViewIfSupported.ts b/src/common/transitionViewIfSupported.ts new file mode 100644 index 00000000..b1edba05 --- /dev/null +++ b/src/common/transitionViewIfSupported.ts @@ -0,0 +1,7 @@ +export const transitionViewIfSupported = (updateCb: () => void) => { + if (document.startViewTransition) { + document.startViewTransition(updateCb); + } else { + updateCb(); + } +}; \ No newline at end of file diff --git a/src/components/ui/ImageEmbed.tsx b/src/components/ui/ImageEmbed.tsx index 9cc390ec..74460fd8 100644 --- a/src/components/ui/ImageEmbed.tsx +++ b/src/components/ui/ImageEmbed.tsx @@ -7,6 +7,7 @@ import { RawAttachment } from "@/chat-api/RawData"; import { createSignal, onCleanup, onMount } from "solid-js"; import env from "@/common/env"; import { ImagePreviewModal } from "./ImagePreviewModal"; +import { transitionViewIfSupported } from "@/common/transitionViewIfSupported"; const ImageEmbedContainer = styled(FlexRow)` user-select: none; @@ -44,6 +45,7 @@ interface ImageEmbedProps { export function ImageEmbed(props: ImageEmbedProps) { const { paneWidth, height, hasFocus } = useWindowProperties(); const { createPortal } = useCustomPortal(); + const [previewModalOpened, setPreviewModalOpened] = createSignal(false); const isGif = () => props.attachment.path?.endsWith(".gif"); const url = (ignoreFocus?: boolean) => { @@ -68,25 +70,43 @@ export function ImageEmbed(props: ImageEmbedProps) { const maxHeight = props.maxHeight ? clamp((props.customHeight || height()) / 2, props.maxHeight) : (props.customHeight || height()) / 2; - return clampImageSize( - props.attachment.width!, - props.attachment.height!, - maxWidth, - maxHeight - ); + + return { + ...clampImageSize( + props.attachment.width!, + props.attachment.height!, + maxWidth, + maxHeight + ), + ...(previewModalOpened() + ? { "view-transition-name": "embed-image" } + : {}), + }; }; const onClicked = () => { if (props.ignoreClick) return; - createPortal((close) => ( - - )); + setPreviewModalOpened(true); + transitionViewIfSupported(() => { + setPreviewModalOpened(false); + createPortal((close) => ( + { + transitionViewIfSupported(() => { + close(); + setPreviewModalOpened(true); + setTimeout(() => { + setPreviewModalOpened(false); + }, 100); + }); + }} + url={url(true)} + origUrl={props.attachment.origSrc} + width={props.attachment.width} + height={props.attachment.height} + /> + )); + }); }; return ( diff --git a/src/components/ui/ImagePreviewModal.tsx b/src/components/ui/ImagePreviewModal.tsx index d2fcaac1..bc68a4f5 100644 --- a/src/components/ui/ImagePreviewModal.tsx +++ b/src/components/ui/ImagePreviewModal.tsx @@ -36,6 +36,7 @@ const ImagePreviewContainer = styled(FlexRow)` img { max-width: 100%; max-height: 100%; + view-transition-name: embed-image; } `; const ImagePreview = styled(FlexRow)` @@ -66,8 +67,8 @@ export function ImagePreviewModal(props: { }) { let imageRef: HTMLImageElement | undefined; let zoomistContainerRef: HTMLImageElement | undefined; - let location = useLocation(); - let navigate = useNavigate(); + const location = useLocation(); + const navigate = useNavigate(); const { createPortal } = useCustomPortal(); createEffect(