From 5b43f19a6cd35529bddf1e7e98b15957dd8ca0ae Mon Sep 17 00:00:00 2001 From: LittleSheep Date: Thu, 6 Nov 2025 01:44:47 +0800 Subject: [PATCH] :drunk: Created a broke view transition --- app/components/AttachmentItem.vue | 17 ++++++ app/pages/files/[id].vue | 95 +++++++++++++++++++++++++++++-- 2 files changed, 107 insertions(+), 5 deletions(-) diff --git a/app/components/AttachmentItem.vue b/app/components/AttachmentItem.vue index fffc42a..7b507ba 100644 --- a/app/components/AttachmentItem.vue +++ b/app/components/AttachmentItem.vue @@ -63,6 +63,23 @@ const imageLoaded = ref(false) const router = useRouter() function openExternally() { + // Capture image position for transition + const img = event?.target as HTMLImageElement + if (img && itemType.value === 'image') { + const rect = img.getBoundingClientRect() + const transitionData = { + src: remoteSource.value, + x: rect.left, + y: rect.top, + width: rect.width, + height: rect.height, + aspectRatio: aspectRatio.value + } + + // Store transition data + sessionStorage.setItem('imageTransition', JSON.stringify(transitionData)) + } + router.push('/files/' + props.item.id) } diff --git a/app/pages/files/[id].vue b/app/pages/files/[id].vue index 9b56e35..00d0f40 100644 --- a/app/pages/files/[id].vue +++ b/app/pages/files/[id].vue @@ -133,6 +133,19 @@ + + +
+ Transitioning image +
@@ -144,10 +157,6 @@ import { downloadAndDecryptFile } from "./secure" import { formatBytes } from "./format" import type { SnCloudFile } from "~/types/api/post" -useHead({ - title: computed(() => fileInfo.value?.name ? `${fileInfo.value.name} - File Preview` : 'File Preview') -}) - const route = useRoute() const error = ref(null) @@ -161,9 +170,18 @@ const infoDialog = ref(false) const secretDialog = ref(false) const dialogPassword = ref("") +// View transition state +const isTransitioning = ref(false) +const transitionImage = ref("") +const transitionStyle = ref>({}) + const api = useSolarNetwork() const fileInfo = ref(null) + +useHead({ + title: computed(() => fileInfo.value?.name ? `${fileInfo.value.name} - File Preview` : 'File Preview') +}) async function fetchFileInfo() { try { let url = "/drive/files/" + fileId + "/info" @@ -176,7 +194,61 @@ async function fetchFileInfo() { error.value = (err as Error).message } } -onMounted(() => fetchFileInfo()) + +function checkForTransition() { + const transitionData = sessionStorage.getItem('imageTransition') + if (transitionData) { + try { + const data = JSON.parse(transitionData) + isTransitioning.value = true + transitionImage.value = data.src + + // Calculate final position (centered in viewport) + const viewportWidth = window.innerWidth + const viewportHeight = window.innerHeight + const finalWidth = Math.min(viewportWidth * 0.9, data.width * (viewportHeight / data.height)) + const finalHeight = finalWidth / data.aspectRatio + const finalX = (viewportWidth - finalWidth) / 2 + const finalY = (viewportHeight - finalHeight) / 2 + + // Set initial position (from original image location) + transitionStyle.value = { + position: 'fixed', + top: `${data.y}px`, + left: `${data.x}px`, + width: `${data.width}px`, + height: `${data.height}px`, + zIndex: 9999, + transition: 'all 0.3s ease-out' + } + + // Animate to final position + requestAnimationFrame(() => { + transitionStyle.value = { + ...transitionStyle.value, + top: `${finalY}px`, + left: `${finalX}px`, + width: `${finalWidth}px`, + height: `${finalHeight}px` + } + + // Hide transition after animation + setTimeout(() => { + isTransitioning.value = false + sessionStorage.removeItem('imageTransition') + }, 300) + }) + } catch (error) { + console.warn('Failed to parse transition data:', error) + sessionStorage.removeItem('imageTransition') + } + } +} + +onMounted(() => { + fetchFileInfo() + checkForTransition() +}) const apiBase = useSolarNetworkUrl() @@ -349,4 +421,17 @@ definePageMeta({ .top-toolbar :deep(.v-app-bar__content) { padding: 0; } + +/* View transition styles */ +.transition-overlay { + pointer-events: none; + z-index: 9999; +} + +.transition-image { + width: 100%; + height: 100%; + object-fit: cover; + border-radius: 4px; +}